Python:类静态成员指向自身?

2 投票
3 回答
642 浏览
提问于 2025-04-17 01:14

可能重复的问题:
我能在描述符的init方法中获取“拥有者”类的引用吗?

代码胜过千言万语:

>>> class ShortRib(object):
>>>     def __init__(self, owner):
>>>         self.owner = owner
>>> 
>>>     ... some more methods and stuff ...
>>> 
>>> 
>>> class Cow(object):
>>>     shortRib = ShortRib(self)
>>> 
>>> 
>>> class BrownCow(Cow):
>>>     pass
>>> 
>>> BrownCow.shortRib.owner
<class '__main__.BrownCow'>

不过这并不奏效,虽然我希望它能工作。基本上,我想让每个类都有一些静态或类变量(我不太确定在这种情况下应该用哪个?),但我需要这些变量知道它们属于哪个类。不幸的是,我无法在类声明的主体中“获取”到这个类。当然,我可以使用装饰器来实现:

>>> def vars(**kwargs):
>>>     def wrap(cls):    
>>>         for k, w in kwargs.items():
>>>             setattr(cls, k, w(cls))
>>>         return cls
>>>     return wrap
>>> 
>>> @vars(shortRib=lambda cls: ShortRib(cls)
>>> class BrownCow(Cow):
>>>     ...
>>>
>>> BrownCow.shortRib.owner

这样是可以的。另一种方法是使用一个类装饰器,它会遍历所有的短肋骨和类似的静态变量,并在类声明完成后设置它们的拥有者。然而,这似乎是一种非常绕的、不直观的方式来完成一个应该很简单的操作:让类的静态成员知道它们属于哪个类。

有没有“正确”的方法来做到这一点?

澄清:

我希望这些成员属于类,而不是属于实例。我想采用一种几乎纯函数式的风格,只使用类来继承共享的行为,而根本不创建它们的实例。实例会让我的函数访问到任意的实例数据,这会破坏我想要的纯函数特性。我可以使用空的实例而不去碰它们,但我觉得使用纯类会更干净。

3 个回答

0

有两种方法可以实现你想要的效果:

  • 你可以在任何类里面重写 __getattr__ 方法,这样当你想要获取某个属性的值时,它就会返回你想要的任何东西。
  • 你可以使用属性(property),它有一个获取器(getter),可以返回你希望返回的对象。

无论是 __getattr__ 方法还是属性,都是可以被继承的。

1

为什么不让Cow类的实例拥有shortRibs,而是让类本身拥有呢?

class ShortRib(object):
    def __init__(self,owner):
        self.owner=owner

class Cow(object):
    def __init__(self):
        self.shortRib=ShortRib(self)

class BrownCow(Cow):
    pass

print(BrownCow().shortRib.owner)
# <__main__.BrownCow object at 0xb76a8d6c>

(否则,你就需要一个类装饰器或者元类——就像你已经提到的那样。但简单的东西总比复杂的好,所以为什么不选择简单呢?)


顺便说一下,如果你真的想用类而不是实例:

class ShortRib(object):
    def __init__(self, owner):
        self.owner = owner

class MetaCow(type):
    def __init__(cls, name, base, attrs):
        super(MetaCow, cls).__init__(name, base, attrs)
        cls.shortRib = ShortRib(cls)

class Cow(object):
    __metaclass__ = MetaCow

class BrownCow(Cow):
    pass

print(Cow.shortRib.owner)
# <class '__main__.Cow'>

print(BrownCow.shortRib.owner)
# <class '__main__.BrownCow'>

使用

class MetaCow(type):
    def __new__(cls, name, base, attrs):

是不对的。type.__new__的签名是

class MetaCow(type):
    def __new__(meta, name, base, attrs):

因为你想修改的是cls的属性,而不是meta,所以应该使用MetaCow.__init__而不是MetaCow.__new__

2

你可以很简单地在 __new__ 里做到这一点:

class ShortRib(object):
    def __init__(self, owner):
        self.owner = owner

class Cow(object):
    shortRib = None
    def __new__(cls, *args, **kwargs):
        if cls.shortRib == None:
            cls.shortRib = ShortRib(cls)
        return super(Cow, cls).__new__(cls, *args, **kwargs)

Cow()
Cow.shortRib.owner

如果你不介意引用 self.__class___,也可以在 __init__ 里实现。

你还可以通过元类来实现这个功能:

class ShortRib(object):
    def __init__(self, owner):
        self.owner = owner

class MetaCow(type):
    def __new__(cls, name, base, attrs):
        attrs['shortRib'] = ShortRib(cls)
        return super(MetaCow, cls).__new__(cls, name, base, attrs)

class Cow(object):
    __metaclass__ = MetaCow

Cow.shortRib.owner

撰写回答