In Python, how do I get a reference to the current class object within a class statement? Example:
def setup_class_members(cls, prefix): setattr(cls, prefix+"_var1", "hello") setattr(cls, prefix+"_var2", "goodbye") class myclass(object): setup_class_members(cls, "coffee") # How to get "cls"? def mytest(self): print(self.coffee_var1) print(self.coffee_var2) x = myclass() x.mytest() >>> hello >>> goodbyeAlternatives that I've written off are:
Use
locals()
: This gives a dict in a class statement that can be written to. This seems to work for classes, however the documentation tells you not to do this. (I might be tempted to go with this alternative if someone can assure me that this will continue to work for some time.)Add members to the class object after the
class
statement: My actual application is to derive a PyQt4QWidget
class with dynamically createdpyqtProperty
class attributes.QWidget
is unusual in that it has a custom metaclass. Very roughly, the metaclass compiles a list ofpyqtProperties
and stores it as additional开发者_JAVA百科 member. For this reason, properties that are added to the class after creation have no effect. An example to clear this up:
from PyQt4 import QtCore, QtGui # works class MyWidget1(QtGui.QWidget): myproperty = QtCore.pyqtProperty(int) # doesn't work because QWidget's metaclass doesn't get to "compile" myproperty class MyWidget2(QtGui.QWidget): pass MyWidget2.myproperty = QtCore.pyqtProperty(int)
Please note that the above will work for most programming cases; my case just happens to be one of those unusual corner cases.
For Python 3, the class must be declared as
class myclass(object, metaclass = Meta):
prefix = "coffee"
...
A few other points:
The metaclass may be a callable, not just a class (Python 2&3)
If the base class of your class already has a non-standard metaclass, you have to make sure you call it's
__init__()
and__new__()
methods instead of type's.The class statement accepts keyword parameters that are passed on to the metaclass (Python 3 only)
A rewrite of mouad's solution in Python 3 using all of the above is...
def MetaFun(name, bases, attr, prefix=None):
if prefix:
attr[prefix+"_var1"] = "hello"
attr[prefix+"_var2"] = "goodbye"
return object.__class__(name, bases, attr)
class myclass(object, metaclass = MetaFun, prefix="coffee"):
def mytest(self):
print(self.coffee_var1)
print(self.coffee_var2)
AFAIK there is two way to do what you want:
Using metaclass, this will create your two variables in class creation time (which i think is what you want):
class Meta(type): def __new__(mcs, name, bases, attr): prefix = attr.get("prefix") if prefix: attr[prefix+"_var1"] = "hello" attr[prefix+"_var2"] = "goodbye" return type.__new__(mcs, name, bases, attr) class myclass(object): __metaclass__ = Meta prefix = "coffee" def mytest(self): print(self.coffee_var1) print(self.coffee_var2)
Create your two class variable in instantiation time:
class myclass(object): prefix = "coffee"
def __init__(self): setattr(self.__class__, self.prefix+"_var1", "hello") setattr(self.__class__, self.prefix+"_var2", "goodbye") def mytest(self): print(self.coffee_var1) print(self.coffee_var2)
N.B: I'm not sure what you want to achieve because if you want to create dynamic variables depending on the prefix
variable why are you accessing like you do in your mytest
method ?! i hope it was just an example.
Two more approaches you might use:
A class decorator.
def setup_class_members(prefix):
def decorator(cls):
setattr(cls, prefix+"_var1", "hello")
setattr(cls, prefix+"_var2", "goodbye")
return cls
return decorator
@setup_class_members("coffee")
class myclass(object):
# ... etc
Especially if you need to add attributes in various combinations, the decorator approach is nice because it does not have any effect on inheritance.
If you are dealing with a small set of of attributes that you wish to combine in various ways, you can use mixin classes. A mixin class is a regular class, it's just intended to "mix in" various attributes to some other class.
class coffee_mixin(object):
coffee_var1 = "hello"
coffee_var2 = "goodbye"
class tea_mixin(object):
tea_var1 = "good morning old bean"
tea_var2 = "pip pip cheerio"
class myclass(coffee_mixin, tea_mixin):
# ... etc
See zope.interface.declarations._implements
for an example of doing this kind of magic. Just be warned that it's a serious maintainability and portability risk.
精彩评论