有没有办法创建一个不会污染其实例的属性命名空间的 Python 类方法



我想提供一个可以在 Python 2.7 类对象上使用的方法,但不会污染其实例的属性命名空间。有什么办法可以做到这一点吗?

>>> class Foo(object):
...   @classmethod
...   def ugh(cls):
...     return 33
...
>>> Foo.ugh()
33
>>> foo = Foo()
>>> foo.ugh()
33

你可以对类方法描述符进行子类类化:

class classonly(classmethod):
    def __get__(self, obj, type):
        if obj: raise AttributeError
        return super(classonly, self).__get__(obj, type)

这是它的行为方式:

class C(object):
    @classonly
    def foo(cls):
        return 42
>>> C.foo()
42
>>> c=C()
>>> c.foo()
AttributeError

这会对描述符调用进行脱糖(相反,它由 __getattribute__ 的默认实现调用):

>>> C.__dict__['foo'].__get__(None, C)
<bound method C.foo of <class '__main__.C'>>
>>> C.__dict__['foo'].__get__(c, type(c))
AttributeError

必读:数据模型 — 实现描述符和描述符操作指南。

> ugh不在命名空间中:

>>> foo.__dict__
{}

但是,属性查找的规则会回退到缺少名称的实例类型。您可以覆盖Foo.__getattribute__以防止出现这种情况。

class Foo(object):
    @classmethod
    def ugh(cls):
        return 33
    def __getattribute__(self, name):
        if name == 'ugh':
            raise AttributeError("Access to class method 'ugh' block from instance")
        return super(Foo,self).__getattribute__(name)

这会产生:

>>> foo = Foo()
>>> foo.ugh()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "tmp.py", line 8, in __getattribute__
    raise AttributeError("Access to class method 'ugh' block from instance")
AttributeError: Access to class method 'ugh' block from instance
>>> Foo.ugh()
33

您必须使用 __getattribute__ ,在任何属性访问时无条件调用,而不是 __getattr__ ,后者仅在正常查找(包括检查类型的命名空间)失败后调用。

Python 有准私有变量,它们使用名称修饰来减少意外访问。形式__name的方法和对象变量将转换为_ClassName__name 。Python 在类上编译方法时会自动更改名称,但不会更改子类的名称。

我可以在类中使用私有方法

>>> class A(object):
...     def __private(self):
...         print('boo')
...     def hello(self):
...         self.__private()
... 
>>> 
>>> A().hello()
boo

但不是在课堂外

>>> A().__private()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'A' object has no attribute '__private'
>>> 

或在子类中

>>> class B(A):
...     def hello2(self):
...         self.__private()
... 
>>> 
>>> B().hello()
boo
>>> B().hello2()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in hello2
AttributeError: 'B' object has no attribute '_B__private'

是的,您可以在元类中创建该方法。

class FooMeta(type):
    # No @classmethod here
    def ugh(cls):
        return 33
class Foo(object):
    __metaclass__ = FooMeta
Foo.ugh()  # returns 33
Foo().ugh()  # AttributeError

请注意,元类是一项强大的功能,如果没有必要,不建议使用元类。 特别是,如果父类具有不同的元类,则多重继承需要特别小心。

最新更新