python - Python 元类的 __new__ 方法中的奇怪行为

标签 python oop behavior metaclass

<分区>

我在 Python 元类的 __new__ 方法中遇到了一个奇怪的行为。我知道以下代码可以正常工作:

def create_property(name, _type):

    def getter(self):
        return self.__dict__.get(name)

    def setter(self, val):
        if isinstance(val, _type):
            self.__dict__[name] = val
        else:
            raise ValueError("Type not correct.")

    return property(getter, setter)


class Meta(type):

    def __new__(cls, clsname, bases, clsdict):
        for key, val in clsdict.items():

            if isinstance(val, type):
                clsdict[key] = create_property(key, val)

        return super().__new__(cls, clsname, bases, clsdict)

但是当避免定义 define_property 函数并将代码放在 __new__ 中的 for 中时,会发生奇怪的事情。以下是修改后的代码:

class Meta(type):

    def __new__(meta, name, bases, clsdict):

        for attr, data_type in clsdict.items():
            if not attr.startswith("_"):

                def getter(self):
                    return self.__dict__[attr]

                def setter(self, val):
                    if isinstance(val, data_type):
                        self.__dict__[attr] = val
                    else:
                        raise ValueError(
                            "Attribute '" + attr + "' must be " + str(data_type) + ".")

                clsdict[attr] = property(getter, setter)

        return super().__new__(meta, name, bases, clsdict)

这个想法是能够创建行为类似于表单的类,即:

class Company(metaclass=Meta):
    name = str
    stock_value = float
    employees = list

if __name__ == '__main__':

    c = Company()
    c.name = 'Apple'
    c.stock_value = 125.78
    c.employees = ['Tim Cook', 'Kevin Lynch']

    print(c.name, c.stock_value, c.employees, sep=', ')

执行时,开始出现不同的错误,例如:

Traceback (most recent call last):
  File "main.py", line 37, in <module>
    c.name = 'Apple'
  File "main.py", line 13, in setter
    if isinstance(val, data_type):
TypeError: isinstance() arg 2 must be a type or tuple of types

Traceback (most recent call last):
  File "main.py", line 38, in <module>
    c.stock_value = 125.78
  File "main.py", line 17, in setter
    "Attribute '" + attr + "' must be " + str(data_type) + ".")
ValueError: Attribute 'name' must be <class 'str'>.

Traceback (most recent call last):
  File "main.py", line 37, in <module>
    c.name = 'Apple'
  File "main.py", line 17, in setter
    "Attribute '" + attr + "' must be " + str(data_type) + ".")
ValueError: Attribute 'stock_value' must be <class 'float'>.

Traceback (most recent call last):
  File "main.py", line 37, in <module>
    c.name = 'Apple'
  File "main.py", line 17, in setter
    "Attribute '" + attr + "' must be " + str(data_type) + ".")
ValueError: Attribute 'employees' must be <class 'list'>.

那么,这里发生了什么?单独定义 create_property 与在 __new__ 方法中定义有什么区别?

最佳答案

这是由于作用域和变量绑定(bind)在 python 中的工作方式。您在访问局部变量的循环中定义一个函数;但是这个局部变量是在函数的执行期间查找的,而不是在其定义期间绑定(bind)的:

fcts = []
for x in range(10):
    def f(): print x
    fcts.append(f)
for f in fcts: f() #prints '9' 10 times, as x is 9 after the loop

正如您所发现的,您可以使用实用函数简单地在当前循环值上创建一个闭包:

fcts = []
def make_f(x):
    def f(): print x
    return f

for x in range(10):
    fcts.append(make_f(x))
for f in fcts: f() #prints '0' to '9'

另一种可能性是(ab)使用默认参数,因为它们是在函数创建期间分配的:

fcts = []
for x in range(10):
    def f(n=x): print n
    fcts.append(f)
for f in fcts: f() #prints '0' to '9'

关于python - Python 元类的 __new__ 方法中的奇怪行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29560988/

相关文章:

python - 如何为列表中的每个项目听写(zip)?

python - 无法在 Python 中导入 GST

python - google.auth.exceptions.DefaultCredentialsError - 不是有效的 json 文件

javascript - 特定祖先的访问方法

perl - Perl 运行时是否有实际的派生类?

java.lang.IllegalStateException : this kind of handler cannot be attached to multiple components

Python:你能逐个添加两个列表吗?

c# - 何时分类而不是区分行为

plone - 有没有办法扩展 Plone Dexterity 的 INameFromTitle 行为?

ruby - 为什么 Array#slice 和 Array#slice!表现不同?