Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/linq/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Class 实例是否可以在不使用_init__;的情况下获得自己的类级可变默认值副本?_Class_Python 3.x_Default Value_Mutable_Metaclass - Fatal编程技术网

Class 实例是否可以在不使用_init__;的情况下获得自己的类级可变默认值副本?

Class 实例是否可以在不使用_init__;的情况下获得自己的类级可变默认值副本?,class,python-3.x,default-value,mutable,metaclass,Class,Python 3.x,Default Value,Mutable,Metaclass,我正在编写一个元类来做一些很酷的事情,它的部分处理是在创建类时检查某些属性是否存在。其中一些是可变的,通常会在\uuuuu init\uuuuu中设置,但由于\uuuuuu init\uuuuu在创建实例之前不会运行,因此元类不会知道将创建属性,并引发错误。我可以这样做: class Test(meta=Meta): mutable = None def __init__(self): self.mutable = list() class Test(metac

我正在编写一个元类来做一些很酷的事情,它的部分处理是在创建类时检查某些属性是否存在。其中一些是可变的,通常会在
\uuuuu init\uuuuu
中设置,但由于
\uuuuuu init\uuuuu
在创建实例之前不会运行,因此元类不会知道将创建属性,并引发错误。我可以这样做:

class Test(meta=Meta):
    mutable = None
    def __init__(self):
        self.mutable = list()
class Test(metaclass=Meta):
    mutable = list()
但这种方法有几个问题:

  • 它强制创建与实例属性类型不同的类属性
  • \uuuu init\uuuu
    仍然必须对class属性进行阴影处理,并且
    Meta
    没有检查这一点
  • 如果
    \uuuu init\uuuuu
    没有对class属性进行阴影处理,我仍然会出现错误(例如
    AttributeError:'NoneType'对象没有属性“append”
    ),而试图避免此类错误是
    Meta
    功能的一部分
  • 最后但并非最不重要的一点是,它是干的
我需要的是一种方式来拥有类似的东西:

class Test(meta=Meta):
    mutable = None
    def __init__(self):
        self.mutable = list()
class Test(metaclass=Meta):
    mutable = list()
但每个实例都会有自己的
mutable
副本

设计目标是:

  • 该属性必须存在于类中(类型无关紧要——它不被选中)
  • 在首次使用该属性之前的某个时间点,会在实例中放置该属性的副本
所需的样本运行:

t1 = Test()
t2 = Test()
t1.mutable.append('one')
t2.mutable.append('two')
t1.mutable  # prints ['one']
t2.mutable  # prints ['two']

关于如何做到这一点,有什么想法吗?

至少有三种方法:

  • 让元类检查所有属性,如果它们是可变表(
    list
    dict
    set
    ,等等)中的一个,用描述符替换该属性,该描述符将在第一次访问时激活,并用可变表的新副本更新实例

  • 提供(1)中的描述符作为编写类时使用的装饰器

  • 让元类向类添加自己的
    \uuuu init\uuu
    方法,该方法在运行时:
    • 调用原始的
      \uuuu init\uuuu
    • 然后检查所需的属性是否存在
  • 缺点(按方法):

  • 如果类具有应在所有实例中共享的可变属性,则需要额外的工作

  • 类中的属性成为类中的函数(可能是思维扭曲;)

  • 将错误点移到类实例化,而不是类定义
  • 我更喜欢(2)是它给类作者提供了完全的控制,简化了类级可变属性应该在所有实例之间共享的情况,并将错误保留在类定义中

    以下是装饰器描述符:

    class ReplaceMutable:
        def __init__(self, func):
            self.func = func
        def __call__(self):
            return self
        def __get__(self, instance, owner):
            if instance is None:
                return self
            result = self.func()
            setattr(instance, self.func.__name__, result)
            return result
    
    和测试类:

    class Test:
        @ReplaceMutable
        def mutable():
            return list()
    
    工作原理:

    property
    一样,
    ReplaceMutable
    是一个描述符对象,其名称与其要替换的属性相同。与
    属性
    不同,它既不定义
    也不定义
    \uuuuuuuuuuuuuuuuuuu
    ,因此当代码尝试在实例中重新绑定名称(
    上述测试中的可变
    )时,Python将允许它这样做。这与缓存描述符背后的想法相同

    ReplaceMutable
    是修饰一个函数(使用所需属性的名称),该函数只返回应该初始化的实例级属性(上面示例中的空
    列表
    )。因此,第一次在实例上查找属性时,它将不会在实例字典中找到,Python将激活描述符;描述符然后调用函数来检索初始对象/数据/任何内容,将其存储在实例中,然后返回。下次在该实例上访问该属性时,它将位于实例字典中,这就是将要使用的内容

    示例代码:

    t1 = Test()
    t2 = Test()
    t1.mutable.append('one')
    t2.mutable.append('two')
    print(t1.mutable)
    print(t2.mutable)
    

    至少有三种方法可以做到这一点:

  • 让元类检查所有属性,如果它们是可变表(
    list
    dict
    set
    ,等等)中的一个,用描述符替换该属性,该描述符将在第一次访问时激活,并用可变表的新副本更新实例

  • 提供(1)中的描述符作为编写类时使用的装饰器

  • 让元类向类添加自己的
    \uuuu init\uuu
    方法,该方法在运行时:
    • 调用原始的
      \uuuu init\uuuu
    • 然后检查所需的属性是否存在
  • 缺点(按方法):

  • 如果类具有应在所有实例中共享的可变属性,则需要额外的工作

  • 类中的属性成为类中的函数(可能是思维扭曲;)

  • 将错误点移到类实例化,而不是类定义
  • 我更喜欢(2)是它给类作者提供了完全的控制,简化了类级可变属性应该在所有实例之间共享的情况,并将错误保留在类定义中

    以下是装饰器描述符:

    class ReplaceMutable:
        def __init__(self, func):
            self.func = func
        def __call__(self):
            return self
        def __get__(self, instance, owner):
            if instance is None:
                return self
            result = self.func()
            setattr(instance, self.func.__name__, result)
            return result
    
    和测试类:

    class Test:
        @ReplaceMutable
        def mutable():
            return list()
    
    工作原理:

    property
    一样,
    ReplaceMutable
    是一个描述符对象,其名称与其要替换的属性相同。与
    属性
    不同,它既不定义
    也不定义
    \uuuuuuuuuuuuuuuuuuu
    ,因此当代码尝试在实例中重新绑定名称(
    上述测试中的可变
    )时,Python将允许它这样做。这与缓存描述符背后的想法相同

    ReplaceMutable
    是修饰一个函数(使用所需属性的名称),该函数只返回应该初始化的实例级属性(上面示例中的空
    列表
    )。因此,第一次在实例上查找属性时,在实例字典中找不到该属性