Python类是否有类似于__setattr__的方法?

4 投票
3 回答
1050 浏览
提问于 2025-04-16 22:11

目前,__setattr__ 这个方法只能在实例(对象)上使用。有没有类似的方法可以在类上使用呢?我问这个问题是因为我想在用户定义类的时候,收集定义的属性列表,并保持它们的顺序,如下所示:

class CfgObj(object):
    _fields = []
    def __setattr__(self, name, value):
        self._fields.append([name, value])
        object.__setattr__(self, name, value)

class ACfg(CfgObj):
    setting1 = Field(str, default='set1', desc='setting1 ...')
    setting2 = Field(int, default=5, desc='setting2...')

我知道上面的代码不会按预期工作,因为 __setattr__ 只能在实例上调用,如下所示:

acfg = ACfg()
acfg.c = 1
acfg._fields == [['c', 1]]

那么,Python 类有没有类似于 __setattr__ 的方法呢?我的主要目的是在用户定义类的时候,按顺序收集定义的属性。

3 个回答

3

是的,不过这样做并不是你想要的方式

class MC(type):
  def __init__(cls, name, bases, dct):
    print dct
    super(MC, cls).__init__(name, bases, dct)

class C(object):
  __metaclass__ = MC
  foo = 42
3

如果你在一个类的元类中定义了 __setattr__(),那么在创建这个类之后,当你给这个类设置属性时,这个方法会被调用:

>>> class Meta(type):
...     def __setattr__(cls, name, value):
...         print "%s=%r" % (name, value)
... 
>>> class A(object):
...     __metaclass__ = Meta
... 
>>> A.a = 1
a=1

但是在定义类的时候,这个方法是不会起作用的,所以这可能不是你想要的效果。

在元类的 __init__() 中获取类的属性是可以的,但这样会失去定义的顺序(还有可能会出现重复定义的问题)。

0

我会这样来解决你的问题,但不是你的提问:我会创建一个Field对象的计数器,并把当前计数器的值设置为刚创建的那个:

class Field(object):
    count = 0
    def __init__(self, value, default=None, desc=None):
        self.value = value
        self.default = default
        self.desc = desc
        # Here comes the magic
        self.nth = Field.count
        Field.count += 1
        # self.created_at = time.time()

然后我会创建一个方法,用来返回所有字段,并按照计数器的值进行排序:

class CfgObj(object):
    def params(self):
        ns = dir(self)
        fs = [getattr(self, field) 
                    for field in ns 
                    if isinstance(getattr(self, field), Field)]
        # fs = sorted(fs, key=lambda f: f.created_at)
        fs = sorted(fs, key=lambda f: f.nth)
        return fs

这个用法很直观:

class ACfg(CfgObj):
    setting1 = Field(str, default='set1', desc='setting1 ...')
    setting2 = Field(int, default=5, desc='setting2...')

print ACfg().params()

显然,这些字段是按照对象创建的时间来排序的,而不是字段创建的时间,但这可能对你来说已经足够了。这样可以吗?

撰写回答