开发者

How do I get a reference for the current class object?

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
>>> goodbye

Alternatives that I've written off are:

  1. 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.)

  2. Add members to the class object after the class statement: My actual application is to derive a PyQt4 QWidget class with dynamically created pyqtProperty class attributes. QWidget is unusual in that it has a custom metaclass. Very roughly, the metaclass compiles a list of pyqtProperties 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:

  1. 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)
    
  2. 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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜