可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I want to provide a method that can be used on a Python 2.7 class object, but does not pollute the attribute namespace of its instances. Is there any way to do this?
>>> class Foo(object): ... @classmethod ... def ugh(cls): ... return 33 ... >>> Foo.ugh() 33 >>> foo = Foo() >>> foo.ugh() 33
回答1:
You could subclass the classmethod descriptor:
class classonly(classmethod): def __get__(self, obj, type): if obj: raise AttributeError return super(classonly, self).__get__(obj, type)
This is how it would behave:
class C(object): @classonly def foo(cls): return 42 >>> C.foo() 42 >>> c=C() >>> c.foo() AttributeError
This desugars to the descriptor call (rather, it is invoked by the default implementation of __getattribute__
):
>>> C.__dict__['foo'].__get__(None, C) <bound method C.foo of <class '__main__.C'>> >>> C.__dict__['foo'].__get__(c, type(c)) AttributeError
Required reading: Data Model ― Implementing Descriptors and Descriptor HowTo Guide.
回答2:
ugh
is not in the namespace:
>>> foo.__dict__ {}
but the rules for attribute lookup fall back to the type of the instance for missing names. You can override Foo.__getattribute__
to prevent this.
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)
This produces:
>>> 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
You must use __getattribute__
, which is called unconditionally on any attribute access, rather than __getattr__
, which is only called after the normal lookup (which includes checking the type's namespace) fails.
回答3:
Yes, you can create the method in the metaclass.
class FooMeta(type): # No @classmethod here def ugh(cls): return 33 class Foo(object): __metaclass__ = FooMeta Foo.ugh() # returns 33 Foo().ugh() # AttributeError
Note that metaclasses are a power feature, and their use is discouraged if unnecessary. In particular, multiple inheritance requires special care if the parent classes have different metaclasses.
回答4:
Python has quasi-private variables that use name-munging to reduce accidental access. Methods and object variables of the form __name
are converted to _ClassName__name
. Python automatically changes the name when compiling methods on the class but doesn't change the name for subclasses.
I can use the private method in a class
>>> class A(object): ... def __private(self): ... print('boo') ... def hello(self): ... self.__private() ... >>> >>> A().hello() boo
But not outside the class
>>> A().__private() Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'A' object has no attribute '__private' >>>
Or in subclasses
>>> 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'