【问题标题】:creating class properties dynamically动态创建类属性
【发布时间】:2020-01-08 16:30:28
【问题描述】:

我正在寻找一种方法来动态创建具有可通过典型实例表示法访问的特定属性的类。

       DynoOne = createClass('DynoOne',props=['A','B'])
       d = DynoOne (database='XYZ')
       d.A = d.B + 1

       DynoTwo = createClass('DynoTwo',props=['A','C','E'])
       q = DynoTwo (database='QRS')
       q.A = q.C + 2*q.E

“道具”如何实际获取和修改的详细信息将被隐藏。 这也使得在新道具可用时添加对它们的访问变得更加容易。

我尝试了以下技术,以了解 python 如何动态生成基本类属性:

Class factory to produce simple struct-like classes?

我对 python 的初步阅读表明,类属性是处理引入 getter/setter 方法以进行访问的一种方法。

不清楚的是如何在工厂构造方法中动态指定属性名(无论是使用装饰器还是显式的property()调用)

例如,使用 property() 。 . .

   class DynamicClass( someBase ):

       def dynamic_getter(self):
           # acquire "stuff"
           return stuff

       def dynamic_setter(self,stuff):
           # store  "stuff"
           pass 

       dynamic_property_name = property(fget=dynamic_getter,fset=dynamic_setter)

当类被声明/构造时,我需要为每个请求的道具创建一个集合。例如,DynoOne 类将为“A”和“B”具有单独的属性/setter/getter。

我怀疑基于模板的 eval() 策略会起作用,但我可能缺少一些更基本和有效的技术。

感谢启蒙和学习机会 :-)

【问题讨论】:

  • 可以说我有以下几点:
  • 我已经做了一些在工厂环境中使用 type() 的实验,特别是使用 init 建立本地实例属性。然而,在感兴趣的情况下,内容需要计算我知道这是使用函数接口python print ('computed content {}'.format(my_instance.getComputed() )) 执行此操作的方法但我仍然希望访问形式为:python print ('computed content {}'.format(my_instance.Computed))
  • 对格式化感到抱歉——我第一次发布并学习导航 API。 . .
  • 嗯,这正是property 的用途,或者如果property 不适合您,您可以实现自己的自定义描述符对象...

标签: python python-3.x class metaprogramming


【解决方案1】:

type 的三个参数允许您动态创建类。所以,一个草图:

def dynamic_getter(self):
    # acquire "stuff"
    return stuff

def dynamic_setter(self,stuff):
    # store  "stuff"
    pass 

DynamicClass =  type('DynamicClass', (SomeBase,), {"dynamic_property_name":property(fget=dynamic_getter,fset=dynamic_setter)})

或者,更具体地说:

In [1]: class SomeBase:
   ...:     def __init__(self):
   ...:         self._foo = 42
   ...:
   ...: def dynamic_getter(self):
   ...:     # acquire "stuff"
   ...:     return self._foo
   ...:
   ...: def dynamic_setter(self,stuff):
   ...:     # store  "stuff"
   ...:     pass
   ...:
   ...: DynamicClass =  type('DynamicClass', (SomeBase,), {"dynamic_property_name":property(fget=dynamic_getter,fset=dynamic_setter)})

In [2]: instance = DynamicClass()

In [3]: instance.dynamic_property_name
Out[3]: 42

注意:type 与其他任何对象一样,实际上是一个类对象,以三参数形式调用它是新类对象实例的构造函数,它是创建其他类对象的类,即元类。实际上,您可以将类定义语句视为上述内容的语法糖。

基于模板的exec(如果你想使用复杂的语句,你需要execeval 只允许表达式)方法也是可行的,如果你发现它更容易使用的话。事实上,这就是collections.namedtuple 在标准库中的工作方式。

注意:您似乎对 Python 中属性的性质感到困惑。类上没有指定实例属性,而是添加一个初始化这些实例属性的函数(通常是__init__),但您可以在任何地方添加实例属性,甚至在方法/类之外.

【讨论】:

  • 啊——这看起来很有希望——现在我知道如何在 type() 调用中设置 property() 关系了!
  • 有没有办法使用像 slots 这样的东西来防止意外的属性引用?即,我想防范类似 ``` my_instance.NOT_SUPPORTED_PROPERTY ``
  • @Tomie 是的,只需使用__slots__
  • @Tomie 但是,如果您尝试访问不存在的属性,Python 总是会抛出错误。 __slots__ 将防止常见的拼写错误,例如 instance.som_var = 42 当您的意思是 instance.some_var = 42
  • 如果我根据道具名称分配__slots__,当type()被调用时我得到ValueError: 'A" in __slots__ conflicts with class variable
【解决方案2】:

在python中,实例变量存储在一个dict下,可用于在创建类的对象时创建动态实例变量。

假设我有一个 dict {'a':1},其中的键 'a' 应该被分配为一个变量,而 1 作为它的值。

__init__函数下,我们可以做like,

self.__dict__['a'] = 1 

我们可以使用 for 循环来遍历字典并执行上述操作来创建动态变量。

根据@chepner 的建议,我们也可以使用setattr 函数来动态设置变量。

【讨论】:

  • setattr(self, 'a', 1) 会更好。如果没有必要,不要直接使用 dunder 名称。
  • 感谢您的信息。已正确编辑答案。
猜你喜欢
  • 2019-05-02
  • 2017-11-07
  • 2017-07-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多