如何获取当前类对象的引用?

5 投票
4 回答
6519 浏览
提问于 2025-04-17 00:56

在Python中,如何在类声明中获取当前类对象的引用?举个例子:

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


我考虑过的一些替代方案有:

  1. 使用 locals():这个方法会在类声明中返回一个可以写入的字典。虽然这在类中似乎有效,但文档上建议不要这样做。(如果有人能保证这个方法在一段时间内仍然有效,我可能会考虑使用这个方案。)

  2. class 声明后添加成员到类对象:我实际的应用是从PyQt4的 QWidget 类派生出一个类,并动态创建 pyqtProperty 类属性。QWidget 有点特别,因为它有一个自定义的元类。简单来说,这个元类会编译一个 pyqtProperties 的列表,并将其作为额外的成员存储。因此,在类创建后添加的属性不会起作用。下面是一个例子来说明这一点:

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)

请注意,上述方法在大多数编程情况下是有效的;我的情况恰好是一个不寻常的特例。

4 个回答

2

你还可以使用另外两种方法:

一种是类装饰器。

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

如果你需要以不同的组合添加属性,使用装饰器的方法很不错,因为它不会影响继承关系。

如果你只需要处理一小部分属性,并希望以不同的方式组合它们,可以使用混合类。混合类其实就是一个普通的类,它的目的是将各种属性“混合”到其他类中。

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
4

对于Python 3,类的声明方式应该是:

class myclass(object, metaclass = Meta):
   prefix = "coffee"
   ...


还有几点需要注意:

  • 元类可以是一个可调用的对象,而不仅仅是一个类(适用于Python 2和3)

  • 如果你的类的基类已经有一个非标准的元类,你需要确保调用它的 __init__()__new__() 方法,而不是直接调用类型的。

  • 类声明可以接受一些关键字参数,这些参数会传递给元类(仅适用于Python 3)

下面是用Python 3重写mouad的解决方案,使用了以上所有内容:

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)
3

据我所知,有两种方法可以实现你想要的:

  1. 使用元类,这样在创建类的时候就会生成你想要的两个变量(我觉得这正是你想要的):

    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. 在实例化的时候创建你的两个类变量:

    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)
    

注意:我不太确定你想要实现什么,因为如果你想根据prefix变量来创建动态变量,那你为什么要像在mytest方法中那样访问呢?希望这只是一个例子。

撰写回答