我想提供一个可以在 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
请注意,元类是一项强大的功能,如果没有必要,不建议使用元类。 特别是,如果父类具有不同的元类,则多重继承需要特别小心。