- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我有以下 python 代码:
class FooMeta(type):
def __setattr__(self, name, value):
print name, value
return super(FooMeta, self).__setattr__(name, value)
class Foo(object):
__metaclass__ = FooMeta
FOO = 123
def a(self):
pass
我希望元类的 __setattr__
被 FOO
和 a
调用。但是,它根本没有被调用。当我在定义类后将某些内容分配给 Foo.whatever
时, 方法被调用。
这种行为的原因是什么?有没有办法拦截在创建类期间发生的分配?在 __new__
中使用 attrs
将不起作用,因为我想 check if a method is being redefined .
最佳答案
类 block 大致是构建字典的语法糖,然后调用元类来构建类对象。
这个:
class Foo(object):
__metaclass__ = FooMeta
FOO = 123
def a(self):
pass
结果很像你写的:
d = {}
d['__metaclass__'] = FooMeta
d['FOO'] = 123
def a(self):
pass
d['a'] = a
Foo = d.get('__metaclass__', type)('Foo', (object,), d)
只有在没有命名空间污染的情况下(实际上也有搜索所有的基来确定元类,或者是否有元类冲突,但我在这里忽略它)。
元类“__setattr__
可以控制当您尝试在其实例之一(类对象)上设置属性时发生的情况,但是在类 block 内您'如果不这样做,您将插入到字典对象中,因此 dict
类控制正在发生的事情,而不是您的元类。所以你运气不好。
除非您使用的是 Python 3.x!在 Python 3.x 中,您可以在元类上定义一个 __prepare__
类方法(或静态方法),它控制在将属性集传递给元类构造函数之前使用什么对象来累积类 block 中的属性集。默认的 __prepare__
只是返回一个普通的字典,但是您可以构建一个自定义的类字典类,它不允许重新定义键,并使用它来累积您的属性:
from collections import MutableMapping
class SingleAssignDict(MutableMapping):
def __init__(self, *args, **kwargs):
self._d = dict(*args, **kwargs)
def __getitem__(self, key):
return self._d[key]
def __setitem__(self, key, value):
if key in self._d:
raise ValueError(
'Key {!r} already exists in SingleAssignDict'.format(key)
)
else:
self._d[key] = value
def __delitem__(self, key):
del self._d[key]
def __iter__(self):
return iter(self._d)
def __len__(self):
return len(self._d)
def __contains__(self, key):
return key in self._d
def __repr__(self):
return '{}({!r})'.format(type(self).__name__, self._d)
class RedefBlocker(type):
@classmethod
def __prepare__(metacls, name, bases, **kwargs):
return SingleAssignDict()
def __new__(metacls, name, bases, sad):
return super().__new__(metacls, name, bases, dict(sad))
class Okay(metaclass=RedefBlocker):
a = 1
b = 2
class Boom(metaclass=RedefBlocker):
a = 1
b = 2
a = 3
运行这个给我:
Traceback (most recent call last):
File "/tmp/redef.py", line 50, in <module>
class Boom(metaclass=RedefBlocker):
File "/tmp/redef.py", line 53, in Boom
a = 3
File "/tmp/redef.py", line 15, in __setitem__
'Key {!r} already exists in SingleAssignDict'.format(key)
ValueError: Key 'a' already exists in SingleAssignDict
一些注意事项:
__prepare__
必须是 classmethod
或 staticmethod
,因为它在元类的实例(您的类)存在之前被调用。<type
仍然需要它的第三个参数是一个真正的 dict
,所以你必须有一个 __new__
方法来转换 SingleAssignDict
到普通的dict
子类化,这可能会避免 (2),但我真的不喜欢这样做,因为像 update
这样的非基本方法不不要尊重您对 __setitem__
等基本方法的覆盖。所以我更喜欢继承 collections.MutableMapping
并包装一个字典。Okay.__dict__
对象是一个普通的字典,因为它是由 type
设置的,而 type
对字典的种类很挑剔它想要。这意味着在类创建后覆盖类属性不会引发异常。如果您想保持类对象字典强制的无覆盖,您可以在 __new__
中的父类(super class)调用之后覆盖 __dict__
属性。遗憾的是,此技术在 Python 2.x 中不可用(我检查过)。 __prepare__
方法没有被调用,这是有意义的,因为在 Python 2.x 中元类是由 __metaclass__
魔法属性而不是类 block 中的特殊关键字决定的;这意味着当元类已知时,用于为类 block 累积属性的字典对象已经存在。
比较 Python 2:
class Foo(object):
__metaclass__ = FooMeta
FOO = 123
def a(self):
pass
大致相当于:
d = {}
d['__metaclass__'] = FooMeta
d['FOO'] = 123
def a(self):
pass
d['a'] = a
Foo = d.get('__metaclass__', type)('Foo', (object,), d)
与 Python 3 相比,要调用的元类是从字典中确定的:
class Foo(metaclass=FooMeta):
FOO = 123
def a(self):
pass
大致相当于:
d = FooMeta.__prepare__('Foo', ())
d['Foo'] = 123
def a(self):
pass
d['a'] = a
Foo = FooMeta('Foo', (), d)
要使用的词典由元类确定。
关于Python 元类 : Why isn't __setattr__ called for attributes set during class definition?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10762088/
type.__setattr__ 用于类,基本上是元类的实例。另一方面,object.__setattr__ 用于类的实例。这是完全明白的。 我看不出这两种方法有什么显着差异,至少在 Python 级
我将命名元组用于不可变数据结构,直到遇到数据类,我在我的用例中更喜欢它(与问题无关)。 现在我了解到它们不是一成不变的!至少严格来说不是。setattr(frozen_dc_obj, "prop",
我试图了解 __setattr__ 如何与类属性一起工作。当我试图覆盖 __setattr__ 以防止在简单类中写入属性时,这个问题就出现了。 我的第一次尝试使用了实例级属性,如下所示: class
我定义了以下类(其中很大一部分被缩短),但我的主要问题是,一旦用其初始化变量定义了此类的对象,我想确保这些变量在类外部是不可更改的...如果进行这样的尝试,我将需要引发 AssertionError
只有当在对象的属性中找不到该属性时,我才想使用 __setattr__,例如 __getattr__。 我真的必须使用try- except吗? def __setattr__(self, name,
如果在类中重写魔术方法 __getattr__,则只有在找不到属性时才调用被重写的方法,否则会出现默认行为。 但是__setattr__好像不是这样,它影响所有的属性。 我正在寻找的是当属性存在时表现
如何在旧式类上执行 __setattr__ 的等效操作? 最佳答案 如果你想做的是为旧样式类的实例设置属性,你可以使用 setattr内置函数,它也应该适用于旧式类。示例 - >>> class Fo
如下面的代码所示,为什么我不能使用 __setattr__ 在作为重载方法的类的一部分的字典上设置值?我预计 b.hello 不会存在。 class MyClass(): datastore
我研究 Python 已经有一段时间了,我开始明白正确覆盖 __setattr__ 可能很麻烦(to say the least !)。 有哪些有效的方法可以确保/向我自己证明覆盖已正确完成?我特别关
在 Python 中,我知道可以这样说 >>> class C: ... def __setattr__(self, name, value): ... print("Hey, you c
我知道在 Python 中可以在运行时向类添加方法: class Test: def __init__(self): self.a=5 test=Test() import t
在 Python 类中,当我使用 __setattr__ 时,它优先于此类(或任何基类)中定义的属性。考虑以下代码: class Test(object): def get_x(self):
我正在尝试覆盖 Python 类的 __setattr__ 方法,因为我想在每次实例属性更改其值时调用另一个函数。但是,我不希望在 __init__ 方法中出现这种行为,因为在此初始化期间,我设置了一
我有以下代码: #-*-coding:utf-8-*- class A(object): def __init__(self, x): self.x = x def _
在 werkzeug 中,我们看到: class Local(object): __slots__ = ('__storage__', '__ident_func__') def __
我想定义一个返回 __setattr__ 的通用实现的函数,该函数使属性不可设置,除非它们是属性(我应该一般测试数据描述符,但那是稍后的事情) )。该函数如下所示: def make_setattr(
我的应用程序在 Windows 上运行,我正在尝试针对 Linux 对其进行配置。 我得到以下 AttributeError: Preferences instances has no attribu
我正在实现一个类的所有预定义函数,当我实现我的__setattr__() 来更新列表时我卡住了 class testop: def __init__(self): self.l
我有一个类的属性,我可以在检查其他属性后更改这些属性,所以我编写了这个类: class MyClass(): def __init__(self, x): self.x = x
在 __setattr__() 中有什么方法可以区分来自类或子/继承类内部的属性集,以及来自当前类或子类外部的属性集? 我想从“外部”更改设置属性的工作方式,在我制作模块的情况下,我希望用户在设置属性
我是一名优秀的程序员,十分优秀!