Is there any way to create a Python class method that does NOT pollute the attribute namespace of its instances?

匿名 (未验证) 提交于 2019-12-03 01:20:02

问题:

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'


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!