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

有没有办法创建一个不会污染其实例的属性名称空间的Python类方法?,python,python-2.7,oop,Python,Python 2.7,Oop,我想提供一个可以在Python2.7类对象上使用的方法,但不会污染其实例的属性名称空间。有没有办法做到这一点 >>> class Foo(object): ... @classmethod ... def ugh(cls): ... return 33 ... >>> Foo.ugh() 33 >>> foo = Foo() >>> foo.ugh() 33 ugh不在命名空间中: >>>

我想提供一个可以在Python2.7类对象上使用的方法,但不会污染其实例的属性名称空间。有没有办法做到这一点

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

ugh
不在命名空间中:

>>> foo.__dict__
{}
但是,属性查找的规则取决于缺少名称的实例的类型。您可以覆盖
Foo.\uuuu getattribute\uuu
以防止出现这种情况

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
foo=foo() >>>foo.ugh() 回溯(最近一次呼叫最后一次): 文件“”,第1行,在 文件“tmp.py”,第8行,在__ raise AttributeError(“从实例访问类方法'ugh'块”) AttributeError:从实例访问类方法“ugh”块 >>>Foo.ugh() 33
您必须使用在任何属性访问时无条件调用的
\uuuuuu getattribute\uuuu
,而不是只有在正常查找(包括检查类型的命名空间)失败后才调用的
\uuuu getattr\uuuuuu

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

class FooMeta(type):
    # No @classmethod here
    def ugh(cls):
        return 33

class Foo(object):
    __metaclass__ = FooMeta

Foo.ugh()  # returns 33
Foo().ugh()  # AttributeError

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

您可以对classmethod描述符进行子类化:

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
这将调用描述符调用(而是由
\uu getattribute\uu
的默认实现调用):

>>C.uuu dict_uuuuuu['foo'].uuuu get_uuuu(无,C)
>>>C.uuu dict_uuuuuuu['foo'].uuuu get_uuuuu(C,类型(C))
属性错误
必读内容:和。

Python使用名称模糊不清来减少意外访问。格式为
\uuu name
的方法和对象变量转换为
\u ClassName\uu 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'
>>> 
>>A()。\uuu private()
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
AttributeError:'对象没有属性'\uu 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'
>>B类(A):
...     def hello2(自我):
...         self.\uuu private()
... 
>>> 
>>>你好
喝倒采
>>>B().hello2()
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
文件“”,第3行,在hello2中
AttributeError:“B”对象没有属性“\u B\u\u private”

从技术上讲,
ugh
不在实例的命名空间中(
foo.\uu dict\uuu
);由于Python的查找算法,可以通过实例简单地访问它?(似乎证实了:
例如,a.x有一个查找链,从a开始,然后键入(a)。\uuuuDict\uuuI['x'],并继续查找(a)类型的基类,不包括元类。
)我想在类对象上创建一些特殊的魔法,但我不想让使用我的类的同事从实例中意外调用它,因为他们很好奇。如果有一种方法可以抑制可见性那就太好了。另一个强大的功能是控制内省的
\uuuuu dir\uuuuu
。@stephernauch当然;我不想阻止那些知道自己在做什么的人,但我确实想限制偶然的错误。在我的例子中,我的同事都是Python初学者,墨菲定律说,他们会找到一种方法,克服任何可能的绊倒危险。(当我假设不是这样的时候,它会导致我必须修复的问题。)使用元类的成本是多少?这似乎正是我正在寻找的功能,只要我能保持它们的功能简单,这将是我想要利用的东西。我将潜在地制造数千个实例对象——如果我使用元类,它们会花费大量额外内存吗?@JasonS:主要问题是元类非常强大,可以用来对类执行许多奇怪的操作(例如,您可以重写特殊方法,如
\uuuu add\uuu
,使
Foo+Foo
成为有意义的表达式)。其中许多会降低代码的可读性。如果您使用它们来覆盖加法或此类简单操作的含义,则会产生混淆,但除此之外,它们只是另一个工具元类,不一定能很好地相互配合。继承自
B
A
元类必须是“兼容的"使用元类
B
;在实践中,它在某种程度上限制了你想在何时何地使用元类。我的大脑在冒烟……你能详细说明一下吗?表面上看这似乎很简单,但我想我不明白
@classmethod
在引擎盖下实际做了什么。嗯。我试过了,但似乎不起作用在Python 2.7Aha中,这是一个超级调用。元类可以覆盖类的定义和对象创建的许多方面,并且可以通过继承产生复杂的副作用。描述符是一种覆盖getattr和setattr的对象类型,不能对类的其余部分产生任何副作用。正如我所知,在AB之外在CMeta层次结构中,stdlib(py3的)只使用了两个元类(一个在enum中,一个在ctypes中),但大量使用了标准的@classmethod装饰符和自定义描述符。我会跟随他们的领导。很酷,谢谢!我对它做了一些调整,为
AttributeError
添加了一条有用的错误消息。
>>> 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'