Reputation: 1269
How do you reference class methods within class variables?
For example:
I have
class UU(object):
map = {
'username': get_username
}
@classmethod
def get_username(cls):
pass
However, get_username
can't be found.
I tried UU.get_username
but that does not appear to work.
Upvotes: 1
Views: 84
Reputation: 1122292
The class body is executed just like a function is, and the local namespace is then turned into the class attributes.
As such normal naming order requirements apply; you cannot reference get_username()
because it is not yet defined.
Moreover, you'd get the unbound classmethod
object even if you moved the map
definition down below the get_username()
definition.
As such, you'd add the method to the mapping after creating the class:
class UU(object):
@classmethod
def get_username(cls):
pass
UU.map = {
'username': UU.get_username
}
Note that this means than from there on out UU.map['username']
is using a classmethod
bound to the UU
class. You'll not get the version on a subclass if you have ever subclassed UU
and provided an override of that method.
You'd have to jump through a lot more hoops to make this work for subclasses; you'd have to make map
a descriptor object so you can bind class methods when looking up values in the mapping, not when defining the mapping:
class BindingMap(dict):
def __get__(self, instance, cls=None):
return {k: v.__get__(instance, cls) if hasattr(v, '__get__') else v for k, v in self.items()}
class UU(object):
@classmethod
def get_username(cls):
pass
map = BindingMap({
'username': get_username,
})
The map will then produce bound class methods on demand, extending to subclasses:
>>> class BindingMap(dict):
... def __get__(self, instance, cls=None):
... return {k: v.__get__(instance, cls) if hasattr(v, '__get__') else v for k, v in self.items()}
...
>>> class UU(object):
... @classmethod
... def get_username(cls):
... pass
... map = BindingMap({
... 'username': get_username,
... })
...
>>> UU.map
{'username': <bound method type.get_username of <class '__main__.UU'>>}
>>> UU.map['username']
<bound method type.get_username of <class '__main__.UU'>>
>>> UU.map['username']()
>>> class UU(object):
... @classmethod
... def get_username(cls):
... print('Username for class {}'.format(cls.__name__))
... map = BindingMap({
... 'username': get_username,
... })
...
>>> UU.map
{'username': <bound method type.get_username of <class '__main__.UU'>>}
>>> UU.map['username']
<bound method type.get_username of <class '__main__.UU'>>
>>> UU.map['username']()
Username for class UU
>>> class Foo(UU):
... pass
...
>>> Foo.map
{'username': <bound method type.get_username of <class '__main__.Foo'>>}
>>> Foo.map['username']
<bound method type.get_username of <class '__main__.Foo'>>
>>> Foo.map['username']()
Username for class Foo
Upvotes: 3
Reputation: 11060
The reason your code doesn't work is because UU's methods are defined after the class variables - so get_username
doesn't exist when you try to add it to the dictionary.
Try this:
class UU(object):
@classmethod
def get_username(cls):
pass
UU.map = {
'username': UU.get_username
}
Judging by your comment on the question, you might find getattr
useful.
Example:
method_name = "get_username"
method = getattr(UU, method_name)
method()
or just
getattr(UU, "get_username")()
No dictionary needed!
Upvotes: 1