If I understand your question correctly, you should be able to reference class A within class A by putting the type annotation in quotes. This is called forward reference.
class A:
# do something
def some_func(self, a: 'A')
# ...
See ref below
- https://github.com/python/mypy/issues/3661
- https://www.youtube.com/watch?v=AJsrxBkV3kc
If I understand your question correctly, you should be able to reference class A within class A by putting the type annotation in quotes. This is called forward reference.
class A:
# do something
def some_func(self, a: 'A')
# ...
See ref below
- https://github.com/python/mypy/issues/3661
- https://www.youtube.com/watch?v=AJsrxBkV3kc
In Python you cannot reference the class in the class body, although in languages like Ruby you can do it.
In Python instead you can use a class decorator but that will be called once the class has initialized. Another way could be to use metaclass but it depends on what you are trying to achieve.
Yes, as you don't have any other reference to the class from within a static method. You could make these class methods instead, using the classmethod decorator:
class C:
@staticmethod
def imstatic():
print("i'm static")
@classmethod
def anotherstatic(cls):
cls.imstatic()
A class method does have a reference to the class.
If you need to refer to the class within a static method you should probably be using a classmethod instead:
class C:
@staticmethod
def imstatic():
print("i'm static")
@classmethod
def imclass(cls):
cls.imstatic()
In the same way that instance methods are "magically" given a reference to the instance as the first argument, class methods are given a reference to the class. You can call them either from an instance or from the class directly, for example both of the following are valid and have the same behavior:
C().imclass()
C.imclass()
That being said, if you do still want to use a static method your current approach is correct, just refer to the class by name.
You don't need to reference the class inside itself, using bar instead of Test.bar will work fine.
class Test:
@staticmethod
def bar():
pass
FOO_DICT = {1: bar}
# You can access this outside of the class by simply using Test.FOO_DICT
print(Test.FOO_DICT)
However, there are cases where you really need to use a class in itself. For example
class Test:
@staticmethod
def bar():
pass
def test_with_other_of_same_instance(self, other: Test):
print(other.FOO_DICT)
FOO_DICT = {1: bar}
In this case,
- I want to define a method that accepts an object of the same Test class.
- I want to use python's type hinting to indicate that the expected argument is an instance of Test. This allows me get editor support and also so tools like pylance and mypy can notify me of possible errors if I passed a argument of wrong data type.
As at the time of this writing, I'll get a NameError: name 'Test' is not defined.
This is because by default I can't use a class within itself (It is possible later versions of python will change its default behavior, hence we won't be needing the solution below by that time).
But if you use Python versions from 3.7+ and you can't reference a class within itself, the simple solution came with PEP 563 - Postponed evaluation of annotations. It is implemented by adding a little line of code at the first line of the file
from __future__ import annotations
# other imports or code come afterwards
class Test:
@staticmethod
def bar():
pass
def test_with_other_of_same_instance(self, other: Test):
print(other.FOO_DICT)
FOO_DICT = {1: bar}
So you can use a class within itself in python by simply including that line at the beginning of the file.
Normally, if you use pylance or any similar tool, you get a warning or error display to show you that you imported something and didn't use it. But not in the case of annotations. You don't have to worry about any warning from your editor.
Note that this must occur at the beginning of the file otherwise you get a SyntaxError and versions earlier than 3.7 won't support this.
If you want the dictionary to be in the class: define the function first and remove Test:
class Test:
@staticmethod
def bar():
pass
FOO_DICT = {1: bar}
class Test:
config: Dict[str, str]
parameters: Dict[str, str]
def default(cls)
return cls(
parameters=get_params(),
config=get_config(file="ATTRIBUTE FROM PARAMETERS"
)
# I tried it like this:
class Test:
config: Dict[str, str]
parameters: Dict[str, str]
def default(cls)
return cls(
parameters=get_params(),
config=get_config(file=cls.parameters.config_file)
)
# AttributeError: type object 'Test' has no attribute 'parameters'I'm fairly new to OOP in Python, and am toying with the idea of making a 2D world that appears 3D by moving things at different rates. Simple enough.
But I find myself wondering if there are times when defining classes within classes is useful. For example, a "layer" object might be something defined within a "viewer" object.
If things are done this way, are there ways for a nested class to have access to methods or attributes of an overarching class? (I was thinking that the "layers" might all have access to where the "viewer" is so that they know how to present themselves. Each "layer" would calculate this for itself, since parallax would change based on apparent distance of that "layer" from the player, but it would all be based on positional attributes the "viewer" has.)
Or is there some better way to organize this? I'm curious to know what kind of organizational decisions people might make and why.
Thank you for sharing your thoughts and/or resources on this.
You need @classmethod rather than @staticmethod - a class method will get passed a reference to the class (where a method will get self), so you can look up attributes on it.
class FooBarBaz:
BAR = 123
@classmethod
def getBar(cls):
return cls.BAR
Actually, there is __class__ in python 3:
Python 3.2.3 (v3.2.3:3d0686d90f55, Apr 10 2012, 11:25:50)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> class A:
... @staticmethod
... def foo():
... print(__class__)
...
>>> A.foo()
<class '__main__.A'>
>>>
See http://www.python.org/dev/peps/pep-3135 for the rationale why it has been added.
No idea about how to achieve the same in py2, I guess this is not possible.
You need to store a reference to the object which has the instance of a Y object:
class X(object):
def __init__(self):
self.count = 5
self.y = Y(self) #create a y passing in the current instance of x
def add2(self):
self.count += 2
class Y(object):
def __init__(self,parent):
self.parent = parent #set the parent attribute to a reference to the X which has it
def modify(self):
self.parent.add2()
Example usage:
>>> x = X()
>>> x.y.modify()
>>> x.count
7
Maybe it's possible for you to use class inheritance? For example:
class X(object):
def __init__(self):
self.count = 5
def add2(self):
self.count += 2
class Y(X):
def __init__(self):
super(Y, self).__init__()
def modify(self):
self.add2()
y = Y() # We now create an instance of Y which is a child class of 'super' class X
y.modify()
print(y.count) # 7
You cannot refer to MetaDataElement while it is being constructed, since it does not yet exist. Thus,
class MetaDataElement:
(MD_INVALID, MD_CATEGORY, MD_TAG) = range(3)
mapInitiator2Type = {'!':MetaDataElement.MD_CATEGORY,
'#':MetaDataElement.MD_TAG}
fails because the very construction of mapInitiator2Type requires MetaDataElement to have attributes, which it does not yet have. You can think of your constants MD_INVALID, etc. as variables that are local to the construction of your class. This is why the following works, as icktoofay wrote:
class MetaDataElement:
(MD_INVALID, MD_CATEGORY, MD_TAG) = range(3)
mapInitiator2Type = {'!': MD_CATEGORY, # MD_CATEGORY is like a local variable!
'#': MD_TAG}
However, you can refer to the class MetaDataElement in any yet un-interpreted piece of code, as in
def method_of_MetaDataElement(self):
print MetaDataElement.MD_TAG
You even have to refer to MetaDataElement, here, because MD_TAG is not a kind of local variable when method_of_MetaDataElement() is executed (MD_TAG was only defined as a kind of local variable during class construction). Once the class MetaDataElement is created, MD_TAG is simply a class attribute, which is why method_of_MetaDataElement() must refer to it as such.
First of all, you're using old-style classes. You should probably use new-style classes, like so:
class MetaDataElement(object):
...
Note the (object). Anyway, though, simply remove the MetaDataElement. when referring to the class attributes. This is what it'd look like when that's done:
class MetaDataElement(object):
(MD_INVALID, MD_CATEGORY, MD_TAG) = range(3)
mapInitiator2Type = {'!': MD_CATEGORY,
'#': MD_TAG}
Use a meta class to automatically set it.
def my_meta(name, bases, attrs):
cls = type(name, bases, attrs)
cls.bar = cls
return cls
class Foo(object):
__metaclass__ = my_meta
>>> print Foo.bar
<class '__main__.Foo'>
You could define a class decorator that replaced placeholder strings with the class being defined:
def fixup(cls):
placeholder = '@' + cls.__name__
for k,v in vars(cls).items():
if v == placeholder:
setattr(cls, k, cls)
return cls
@fixup
class Foo(object):
bar = '@Foo'
print('Foo.bar: {!r}'.format(Foo.bar)) # -> Foo.bar: <class '__main__.Foo'>
Another alternative would be to use the __init_subclass__() special method which was introduced in Python 3.6 to create a base class and then derive your class from it instead of the generic object:
class Base(object):
def __init_subclass__(cls, /, **kwargs):
super().__init_subclass__(**kwargs)
cls.bar = cls
class Foo(Base):
pass
print('Foo.bar: {!r}'.format(Foo.bar)) # -> Foo.bar: <class '__main__.Foo'>
There's no way to refer to the class that's currently being defined. There should really be keywords referring to the current scope, eg. __this_class__ for the innermost class being defined and __this_func__ for the innermost function, so classes and functions can cleanly refer to themselves without having to repeat their name.
You could move the definition of columns out of the class body:
class SomeObject:
def __init__(self, values):
self.values = values
...
SomeObject.columns = [
(lambda x: x['type'], 'Type'),
(lambda x: 'http://localhost/view?id=%s' % x['id'], 'Link'),
(SomeObject.helper_function, 'Data'),
]
By the way, please always use at least 4-space indentation. Anything less is very hard to read.
Why not populate columns in init() and use self?
def __init__(self, values):
self.values = values
self.columns = [
(lambda x: x['type'], 'Type'),
(lambda x: 'http://localhost/view?id=%s' % x['id'], 'Link'),
(self.helper_function, 'Data'),
]