如何动态地向类添加属性?


215

目标是创建一个行为类似db结果集的模拟类。

因此,例如,如果数据库查询使用dict表达式返回{'ab':100, 'cd':200},那么我想看看:

>>> dummy.ab
100

刚开始我以为我可以这样做:

ks = ['ab', 'cd']
vs = [12, 34]
class C(dict):
    def __init__(self, ks, vs):
        for i, k in enumerate(ks):
            self[k] = vs[i]
            setattr(self, k, property(lambda x: vs[i], self.fn_readyonly))

    def fn_readonly(self, v)
        raise "It is ready only"

if __name__ == "__main__":
    c = C(ks, vs)
    print c.ab

c.ab返回一个属性对象。

用替换该setattrk = property(lambda x: vs[i])根本没有用。

那么在运行时创建实例属性的正确方法是什么?

PS我知道如何使用__getattribute__方法?


2
您的代码中有一些错别字:fn_readonly的定义需要一个:__init__引用self.fn_readyonly
mhawke

你是对的。我在最后一分钟添加了该setter函数,以强调在运行时创建属性的原因。
Anthony Kong

我在初始化时创建属性时遇到的主要问题是,在某些情况下,如果我在之后快速调用帮助程序,或者出现问题,尽管它们确实存在,但我还是会得到一个错误,指出它们并不存在。在下面的解决方案中,我创建2个类。一个作为基础/父(我试图找到一种避免的解决方案),以及一个主要对象,它扩展了基础/父。然后,在主对象中,无需初始化,就调用我的AccessorFunc创建器,该创建器创建属性,辅助函数等。
Acecool

即:类ExampleBase:通过;类Example(ExampleBase):__x = Accessor(ExampleBase,'x','X',123); ---它将在x下创建一个属性,并使用X命名函数,因此使用GetX,SetX等...,并为该属性设置.x,._ x和.__ x。因此.x本身就是要传递数据的属性(通过self.x = 123获取/设置;或者通过self.x进行输出)。我将self._x用于存储的RAW数据,因此可以轻松访问它,因为我还允许分配默认值,而无需在存储的数据中设置它们。因此_x可能为None,.x可能返回123。并且.__ x链接到访问器
Acecool

这是创建动态属性和动态函数的基本版本的链接-该文件具有许多其他版本的链接。一种是AccessorFunc系统,它使用一个函数来创建辅助函数(一个用于函数,一个用于属性,一个用于两个单独的元素-因此,它在该文件中的任何内容中均不使用代码缩短功能)。其他文件具有它:dropbox.com/s/phnnuavssmzeqrr/dynamic_properties_simple.py?dl=0
Acecool

Answers:


333

我想我应该扩大这个答案,因为我年龄较大,比较聪明,并且知道发生了什么事。迟到总比不到好。

可以动态地向类添加属性。但这很重要:您必须将其添加到类中

>>> class Foo(object):
...     pass
... 
>>> foo = Foo()
>>> foo.a = 3
>>> Foo.b = property(lambda self: self.a + 1)
>>> foo.b
4

property实际上,A 是称为描述符的事物的简单实现。该对象为给定类的给定属性提供自定义处理。Kinda就像一种从if树上拔出一棵大树的方法__getattribute__

当我要求foo.b在上面的例子中,Python看到的是,b在类实现限定的描述符协议哪位只是意味着它是一个对象__get____set____delete__方法。描述符声明负责处理该属性,因此Python调用Foo.b.__get__(foo, Foo),并将返回值作为属性值传递回给您。在的情况下property,每一种方法只是调用fgetfsetfdel你传递给property构造函数。

描述符实际上是Python公开其整个OO实现的基本方法。实际上,还有一种比更为常见的描述符property

>>> class Foo(object):
...     def bar(self):
...         pass
... 
>>> Foo().bar
<bound method Foo.bar of <__main__.Foo object at 0x7f2a439d5dd0>>
>>> Foo().bar.__get__
<method-wrapper '__get__' of instancemethod object at 0x7f2a43a8a5a0>

谦虚方法只是另一种描述符。它__get__以调用实例为第一个参数;实际上,它是这样做的:

def __get__(self, instance, owner):
    return functools.partial(self.function, instance)

无论如何,我怀疑这就是为什么描述符仅在类上起作用的原因:它们是首先为类提供支持的东西的形式化形式。它们甚至是规则的例外:您显然可以为一个类分配描述符,而类本身就是type!的实例。实际上,试图读取Foo.b静态调用property.__get__;当描述符作为类属性访问时,返回它们自己只是惯用的。

我认为几乎所有Python的OO系统都可以用Python表示非常酷。:)

哦,如果您有兴趣的话,我写了一篇关于描述符冗长博客文章


35
无需添加add_property方法。setattr (Foo, 'name', property (func))
Courtney D

8
您的“但这就是重点……”为我节省了数小时的工作。谢谢。
马特·豪威尔,2015年

2
如果要在单个实例上定义属性,则可以在运行时创建一个类并修改__class__
威尔弗雷德·休斯

1
@ myproperty.setter呢?如何动态添加?
LRMAAX '19

您无需向初始化的对象添加属性。这样做可能意味着它仅附着在实例上,但我必须仔细检查。我知道我遇到了一个类似的问题,即我的动态属性仅是实例,我还得到了一个静态设置,而我想要的对象是静态设置,以便将来的初始化将使用它们。我的帖子在下面,它创建了辅助函数,以及轻松访问所有内容的简便方法。.x代表属性,._ x代表getter / setter使用的原始数据(可以为None),.__ x代表访问器对象。
Acecool

57

目标是创建一个行为类似db结果集的模拟类。

因此,您需要一本可以将a ['b']拼写为ab的字典吗?

这很简单:

class atdict(dict):
    __getattr__= dict.__getitem__
    __setattr__= dict.__setitem__
    __delattr__= dict.__delitem__

1
在更一般的设置中,此功能用途有限。如果dict具有多层结构,例如d = {'a1':{'b':'c'},'a2':...},那么您可以执行d.a1或d.a2,但是可以做T d.a1.b
Shreyas

1
有一点要记住的是,这允许使用相同的名称为字典方法或属性的属性设置属性值,但不允许再次以同样的方式检索值:d.items = 1d.items返回<built-in method items of atdict object at ...>。您仍然可以代替来d["items"]使用或使用,但是这会阻止使用大多数dict方法。__getattribute____getattr__
Marcono1234 '17

只需使用munch库!(一堆叉子)
Brian Peterson

38

似乎您可以使用来更简单地解决此问题namedtuple,因为您提前知道了整个字段列表。

from collections import namedtuple

Foo = namedtuple('Foo', ['bar', 'quux'])

foo = Foo(bar=13, quux=74)
print foo.bar, foo.quux

foo2 = Foo()  # error

如果您绝对需要编写自己的setter,则必须在类级别进行元编程。property()在实例上不起作用。


好想法。不幸的是,此刻我一直使用python 2.4。
Anthony Kong


2
撰写该文章的人namedtuple值得一提的是,使它成为忠实的面向对象原则时既流畅又优雅。
基思·品森

4
抱歉,此答案充其量仅适用于特殊情况,即仅由只读属性组成的一个通缉犯类都事先知道。换句话说,我认为它没有解决更广泛的问题,即如何在运行时将常规属性(不仅仅是只读属性)添加到类中(其他“附加”答案的当前版本也是如此)由作者发布)。
martineau 2013年

@martineau所以...将更多参数传递给property()?在这两个答案中,没有什么是只读属性所特有的。
伊芙2013年

32

您不需要为此使用属性。只需重写__setattr__即可使其只读。

class C(object):
    def __init__(self, keys, values):
        for (key, value) in zip(keys, values):
            self.__dict__[key] = value

    def __setattr__(self, name, value):
        raise Exception("It is read only!")

多田

>>> c = C('abc', [1,2,3])
>>> c.a
1
>>> c.b
2
>>> c.c
3
>>> c.d
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'C' object has no attribute 'd'
>>> c.d = 42
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __setattr__
Exception: It is read only!
>>> c.a = 'blah'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __setattr__
Exception: It is read only!

9

如何动态地向python类添加属性?

假设您有一个对象要添加属性。通常,当我需要开始管理对具有下游用法的代码中的属性的访问时,我想使用属性,以便可以维护一致的API。现在,我通常将它们添加到定义对象的源代码中,但是让我们假设您没有该访问权限,或者您需要以编程方式真正地动态选择函数。

建立课程

使用基于的文档property的示例,让我们创建一个具有“隐藏”属性的对象类并创建一个实例:

class C(object):
    '''basic class'''
    _x = None

o = C()

在Python中,我们期望有一种显而易见的处理方式。但是,在这种情况下,我将展示两种方式:使用装饰符表示法和不使用装饰符表示法。首先,没有装饰符号。这对于动态获取,设置或删除程序可能更为有用。

动态(又名猴子修补)

让我们为我们的班级创建一些:

def getx(self):
    return self._x

def setx(self, value):
    self._x = value

def delx(self):
    del self._x

现在,我们将这些分配给属性。请注意,我们可以在此处以编程方式选择函数,回答动态问题:

C.x = property(getx, setx, delx, "I'm the 'x' property.")

和用法:

>>> o.x = 'foo'
>>> o.x
'foo'
>>> del o.x
>>> print(o.x)
None
>>> help(C.x)
Help on property:

    I'm the 'x' property.

装饰工

我们可以使用装饰器符号做与上面相同的操作,但是在这种情况下,我们必须将所有方法都命名为相同的名称(我建议将其与属性保持相同),因此,程序化赋值并不是那么简单它使用上面的方法:

@property
def x(self):
    '''I'm the 'x' property.'''
    return self._x

@x.setter
def x(self, value):
    self._x = value

@x.deleter
def x(self):
    del self._x

并将属性对象及其提供的设置器和删除器分配给该类:

C.x = x

和用法:

>>> help(C.x)
Help on property:

    I'm the 'x' property.

>>> o.x
>>> o.x = 'foo'
>>> o.x
'foo'
>>> del o.x
>>> print(o.x)
None

5

在这个Stack Overflow帖子上问了一个相似的问题以创建一个创建简单类型的类工厂。结果是这个答案具有班级工厂的工作版本。这是答案的片段:

def Struct(*args, **kwargs):
    def init(self, *iargs, **ikwargs):
        for k,v in kwargs.items():
            setattr(self, k, v)
        for i in range(len(iargs)):
            setattr(self, args[i], iargs[i])
        for k,v in ikwargs.items():
            setattr(self, k, v)

    name = kwargs.pop("name", "MyStruct")
    kwargs.update(dict((k, None) for k in args))
    return type(name, (object,), {'__init__': init, '__slots__': kwargs.keys()})

>>> Person = Struct('fname', 'age')
>>> person1 = Person('Kevin', 25)
>>> person2 = Person(age=42, fname='Terry')
>>> person1.age += 10
>>> person2.age -= 10
>>> person1.fname, person1.age, person2.fname, person2.age
('Kevin', 35, 'Terry', 32)
>>>

您可以使用此方法的一些变体来创建默认值,这是您的目标(该问题中也有一个答案与此相关)。


4

不知道我是否完全理解这个问题,但是您可以在运行时使用类的内置实例属性进行修改__dict__

class C(object):
    def __init__(self, ks, vs):
        self.__dict__ = dict(zip(ks, vs))


if __name__ == "__main__":
    ks = ['ab', 'cd']
    vs = [12, 34]
    c = C(ks, vs)
    print(c.ab) # 12

本质上,我的问题是找出是否有可能在运行时创建新属性。共识似乎是负面的。您的建议当然简单实用。(与使用dict的其他答案相同)
Anthony Kong

一个简单的答案也是:self.__dict__[key] = value
艾伦·卡尔森

4

对于那些来自搜索引擎的人来说,这是我在谈论动态属性时正在寻找的两件事:

class Foo:
    def __init__(self):
        # we can dynamically have access to the properties dict using __dict__
        self.__dict__['foo'] = 'bar'

assert Foo().foo == 'bar'


# or we can use __getattr__ and __setattr__ to execute code on set/get
class Bar:
    def __init__(self):
        self._data = {}
    def __getattr__(self, key):
        return self._data[key]
    def __setattr__(self, key, value):
        self._data[key] = value

bar = Bar()
bar.foo = 'bar'
assert bar.foo == 'bar'

__dict__如果要放置动态创建的属性,则很好。__getattr__仅在需要该值时才做某事是很好的,例如查询数据库。设置/获取组合可以简化对类中存储的数据的访问(就像上面的示例一样)。

如果只需要一个动态属性,请查看property()内置函数。


4

您不能property()在运行时向实例添加新实例,因为属性是数据描述符。相反,您必须动态创建一个新类或重载__getattribute__以处理实例上的数据描述符。


错了 您可以将属性添加到类中,然后从方法中访问它。
艾哈迈德(Ahmed)

2

最好的方法是通过定义__slots__。这样,您的实例就不能具有新属性。

ks = ['ab', 'cd']
vs = [12, 34]

class C(dict):
    __slots__ = []
    def __init__(self, ks, vs): self.update(zip(ks, vs))
    def __getattr__(self, key): return self[key]

if __name__ == "__main__":
    c = C(ks, vs)
    print c.ab

那打印 12

    c.ab = 33

这给出了: AttributeError: 'C' object has no attribute 'ab'


2

另一个例子,如何达到预期的效果

class Foo(object):

    _bar = None

    @property
    def bar(self):
        return self._bar

    @bar.setter
    def bar(self, value):
        self._bar = value

    def __init__(self, dyn_property_name):
        setattr(Foo, dyn_property_name, Foo.bar)

所以现在我们可以做类似的事情:

>>> foo = Foo('baz')
>>> foo.baz = 5
>>> foo.bar
5
>>> foo.baz
5

2

这是一个解决方案:

  • 允许将属性名称指定为string,因此它们可以来自某些外部数据源,而不是在程序中列出的所有名称。
  • 在定义类时添加属性,而不是在每次创建对象时添加属性

定义了类之后,只需执行以下操作即可向其动态添加属性:

setattr(SomeClass, 'propertyName', property(getter, setter))

这是一个经过Python 3测试的完整示例:

#!/usr/bin/env python3

class Foo():
  pass

def get_x(self):
  return 3

def set_x(self, value):
  print("set x on %s to %d" % (self, value))

setattr(Foo, 'x', property(get_x, set_x))

foo1 = Foo()
foo1.x = 12
print(foo1.x)

1

您可以使用以下代码通过字典对象更新类属性:

class ExampleClass():
    def __init__(self, argv):
        for key, val in argv.items():
            self.__dict__[key] = val

if __name__ == '__main__':
    argv = {'intro': 'Hello World!'}
    instance = ExampleClass(argv)
    print instance.intro

1

这与OP想要的有所不同,但是我动了动脑筋,直到找到可行的解决方案为止,所以我在这里为下一个家伙/女友

我需要一种方法来指定动态的setter和getter。

class X:
    def __init__(self, a=0, b=0, c=0):
        self.a = a
        self.b = b
        self.c = c

    @classmethod
    def _make_properties(cls, field_name, inc):
        _inc = inc

        def _get_properties(self):
            if not hasattr(self, '_%s_inc' % field_name):
                setattr(self, '_%s_inc' % field_name, _inc)
                inc = _inc
            else:
                inc = getattr(self, '_%s_inc' % field_name)

            return getattr(self, field_name) + inc

        def _set_properties(self, value):
            setattr(self, '_%s_inc' % field_name, value)

        return property(_get_properties, _set_properties)

我提前知道我的领域,所以我要创建我的属性。注意:您不能执行此PER实例,这些属性将存在于类中!!!

for inc, field in enumerate(['a', 'b', 'c']):
    setattr(X, '%s_summed' % field, X._make_properties(field, inc))

让我们现在测试一下。

x = X()
assert x.a == 0
assert x.b == 0
assert x.c == 0

assert x.a_summed == 0  # enumerate() set inc to 0 + 0 = 0
assert x.b_summed == 1  # enumerate() set inc to 1 + 0 = 1
assert x.c_summed == 2  # enumerate() set inc to 2 + 0 = 2

# we set the variables to something
x.a = 1
x.b = 2
x.c = 3

assert x.a_summed == 1  # enumerate() set inc to 0 + 1 = 1
assert x.b_summed == 3  # enumerate() set inc to 1 + 2 = 3
assert x.c_summed == 5  # enumerate() set inc to 2 + 3 = 5

# we're changing the inc now
x.a_summed = 1 
x.b_summed = 3 
x.c_summed = 5

assert x.a_summed == 2  # we set inc to 1 + the property was 1 = 2
assert x.b_summed == 5  # we set inc to 3 + the property was 2 = 5
assert x.c_summed == 8  # we set inc to 5 + the property was 3 = 8

令人困惑吗?是的,很抱歉,我无法提出任何有意义的现实示例。同样,这也不适合那些轻松的人。


如果我没记错的话,我确实在所有测试过程中都找到了一种创建STATIC类型属性/动态添加g / setter的方法。我必须遍历所有以前的内容,但是绝对可以添加所有实例之间共享的内容。至于按实例创建流程...我很确定您可以这样做,以便一个实例拥有其他实例没有的东西。我必须进行验证,但是我也遇到了类似的问题(在我的第一次尝试中,我犯了一个错误,该错误导致创建了函数,但是并非所有实例都存在缺陷)
Acecool

同样,欢迎所有可能的解决方案,因为这是一种知识回购。看到不同的人为问题创建解决方案的不同方式也很令人兴奋。我的解决方案很多,您将其简化为易于共享的内容。我也做了我的一个较小的变体-它应该在本主题中的某个地方-我只是意识到这不是我张贴的那个:-)...
Acecool

0

这似乎可行(但请参见下文):

class data(dict,object):
    def __init__(self,*args,**argd):
        dict.__init__(self,*args,**argd)
        self.__dict__.update(self)
    def __setattr__(self,name,value):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be set"%(name,self.__class__.__name__)
    def __delattr__(self,name):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be deleted"%(name,self.__class__.__name__)

如果您需要更复杂的行为,请随时编辑答案。

编辑

对于大型数据集,以下内容可能会更节省内存:

class data(dict,object):
    def __init__(self,*args,**argd):
        dict.__init__(self,*args,**argd)
    def __getattr__(self,name):
        return self[name]
    def __setattr__(self,name,value):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be set"%(name,self.__class__.__name__)
    def __delattr__(self,name):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be deleted"%(name,self.__class__.__name__)

0

为了回答问题的主旨,您希望将字典中的只读属性作为不可变数据源:

目标是创建一个行为类似db结果集的模拟类。

因此,例如,如果数据库查询使用dict表达式返回 {'ab':100, 'cd':200},那么我将看到

>>> dummy.ab
100

我将演示如何使用namedtuple来自collections模块的a来完成此任务:

import collections

data = {'ab':100, 'cd':200}

def maketuple(d):
    '''given a dict, return a namedtuple'''
    Tup = collections.namedtuple('TupName', d.keys()) # iterkeys in Python2
    return Tup(**d)

dummy = maketuple(data)
dummy.ab

退货 100


0
class atdict(dict):
  def __init__(self, value, **kwargs):
    super().__init__(**kwargs)
    self.__dict = value

  def __getattr__(self, name):
    for key in self.__dict:
      if type(self.__dict[key]) is list:
        for idx, item in enumerate(self.__dict[key]):
          if type(item) is dict:
            self.__dict[key][idx] = atdict(item)
      if type(self.__dict[key]) is dict:
        self.__dict[key] = atdict(self.__dict[key])
    return self.__dict[name]



d1 = atdict({'a' : {'b': [{'c': 1}, 2]}})

print(d1.a.b[0].c)

输出为:

>> 1

0

kjfletch扩展想法

# This is my humble contribution, extending the idea to serialize
# data from and to tuples, comparison operations and allowing functions
# as default values.

def Struct(*args, **kwargs):
    FUNCTIONS = (types.BuiltinFunctionType, types.BuiltinMethodType, \
                 types.FunctionType, types.MethodType)
    def init(self, *iargs, **ikwargs):
        """Asume that unamed args are placed in the same order than
        astuple() yields (currently alphabetic order)
        """
        kw = list(self.__slots__)

        # set the unnamed args
        for i in range(len(iargs)):
            k = kw.pop(0)
            setattr(self, k, iargs[i])

        # set the named args
        for k, v in ikwargs.items():
            setattr(self, k, v)
            kw.remove(k)

        # set default values
        for k in kw:
            v = kwargs[k]
            if isinstance(v, FUNCTIONS):
                v = v()
            setattr(self, k, v)

    def astuple(self):
        return tuple([getattr(self, k) for k in self.__slots__])

    def __str__(self):
        data = ['{}={}'.format(k, getattr(self, k)) for k in self.__slots__]
        return '<{}: {}>'.format(self.__class__.__name__, ', '.join(data))

    def __repr__(self):
        return str(self)

    def __eq__(self, other):
        return self.astuple() == other.astuple()

    name = kwargs.pop("__name__", "MyStruct")
    slots = list(args)
    slots.extend(kwargs.keys())
    # set non-specific default values to None
    kwargs.update(dict((k, None) for k in args))

    return type(name, (object,), {
        '__init__': init,
        '__slots__': tuple(slots),
        'astuple': astuple,
        '__str__': __str__,
        '__repr__': __repr__,
        '__eq__': __eq__,
    })


Event = Struct('user', 'cmd', \
               'arg1', 'arg2',  \
               date=time.time, \
               __name__='Event')

aa = Event('pepe', 77)
print(aa)
raw = aa.astuple()

bb = Event(*raw)
print(bb)

if aa == bb:
    print('Are equals')

cc = Event(cmd='foo')
print(cc)

输出:

<Event: user=pepe, cmd=77, arg1=None, arg2=None, date=1550051398.3651814>
<Event: user=pepe, cmd=77, arg1=None, arg2=None, date=1550051398.3651814>
Are equals
<Event: user=None, cmd=foo, arg1=None, arg2=None, date=1550051403.7938335>

0

尽管给出了很多答案,但我找不到我满意的答案。我想出了自己的解决方案,该解决方案property适用于动态案例。回答原始问题的来源:

#!/usr/local/bin/python3

INITS = { 'ab': 100, 'cd': 200 }

class DP(dict):
  def __init__(self):
    super().__init__()
    for k,v in INITS.items():
        self[k] = v 

def _dict_set(dp, key, value):
  dp[key] = value

for item in INITS.keys():
  setattr(
    DP,
    item,
    lambda key: property(
      lambda self: self[key], lambda self, value: _dict_set(self, key, value)
    )(item)
  )

a = DP()
print(a)  # {'ab': 100, 'cd': 200}
a.ab = 'ab100'
a.cd = False
print(a.ab, a.cd) # ab100 False

0

对我有用的是这样的:

class C:
    def __init__(self):
        self._x=None

    def g(self):
        return self._x

    def s(self, x):
        self._x = x

    def d(self):
        del self._x

    def s2(self,x):
        self._x=x+x

    x=property(g,s,d)


c = C()
c.x="a"
print(c.x)

C.x=property(C.g, C.s2)
C.x=C.x.deleter(C.d)
c2 = C()
c2.x="a"
print(c2.x)

输出量

a
aa

-1

最近,我遇到了一个类似的问题,即我想出的解决方案使用了它,__getattr__并且__setattr__对于我希望它处理的属性,所有其他信息都传递给了原始文件。

class C(object):
    def __init__(self, properties):
        self.existing = "Still Here"
        self.properties = properties

    def __getattr__(self, name):
        if "properties" in self.__dict__ and name in self.properties:
            return self.properties[name] # Or call a function, etc
        return self.__dict__[name]

    def __setattr__(self, name, value):
        if "properties" in self.__dict__ and name in self.properties:
            self.properties[name] = value
        else:
            self.__dict__[name] = value

if __name__ == "__main__":
    my_properties = {'a':1, 'b':2, 'c':3}
    c = C(my_properties)
    assert c.a == 1
    assert c.existing == "Still Here"
    c.b = 10
    assert c.properties['b'] == 10

我对此进行了调查,但是从技术上讲,您正在遍历获取和设置助手中的列表。因此,每个呼叫的速度都会变慢,因为首先要从列表中查找它,而不是直接访问它。除非Python为您自动映射;否则,可能可以,但是我还不确定是否对此进行基准测试,但是在尝试之前,这是我的担忧。其次,通过这样做,您必须以另一种方式定义帮助程序。您也不能锁定数据类型和/或值,而不必以大型字典或大量额外的行作为结尾。
Acecool

即:我要么必须创建一个基类,然后扩展使用该系统的所有子级,要么我必须向所有内容添加s / getattr魔术函数,并每次都复制该系统。属性的声明还意味着您必须以一种方式进行设置,并且如果您想要任何其他支持(如我在上面列出的那样),例如允许或禁止分配数据的数据类型和/或值保护,以及其他帮助器, ,那么您必须对其进行编码。当然,您可以使系统在行为上相似,但是最终您声明的位置略有不同且体积较大。
Acecool

-1

这是通过编程创建属性对象的简单示例。

#!/usr/bin/python3

class Counter:
    def __init__(self):
        cls = self.__class__
        self._count = 0
        cls.count = self.count_ref()

    def count_get(self):
        print(f'count_get: {self._count}')
        return self._count

    def count_set(self, value):
        self._count = value
        print(f'count_set: {self._count}')

    def count_del(self):
        print(f'count_del: {self._count}')

    def count_ref(self):
        cls = self.__class__
        return property(fget=cls.count_get, fset=cls.count_set, fdel=cls.count_del)

counter = Counter()

counter.count
for i in range(5):
    counter.count = i
del counter.count

'''
output
======
count_get: 0
count_set: 0
count_set: 1
count_set: 2
count_set: 3
count_set: 4
count_del: 4
'''

-2

动态附加属性的唯一方法是使用新属性创建一个新类及其实例。

class Holder: p = property(lambda x: vs[i], self.fn_readonly)
setattr(self, k, Holder().p)

1
这似乎不起作用。它将分配属性的结果,而不是属性本身。
2013年

这是不正确的。我将动态属性附加到我的系统中,而不必初始化类。初始化为x = Example(),然后将属性添加到x。
Acecool

如果看一下我的代码,您会看到我使用ExampleBaseBase类:pass,然后使用ExampleExample(ExampleBase)类:...然后将属性附加到ExampleBase,因为名称随后存在,并且因为Example从它扩展而来,它可以访问所有内容。我将__ var用于访问器帮助程序,以便能够直接访问访问器对象,我使用_表示存储的数据(raw),该数据可以为None,对于通过getter的实际属性不使用下划线。我可以使用动态添加的函数来调用getter函数,也可以使用该属性。一切都无需先进行。
Acecool

注意:我确实提到了它-但是定义的定义意味着引用存在于名称空间中-即:class Example(Object):pass ...它存在,但尚未初始化。初始化意味着等等= Example(); 现在,该对象已被“复制”并构造,然后存储为引用。---如果执行此操作,则动态添加的功能/属性应仅在实例中-我遇到的问题是即使功能存在,在某些情况下我也会收到错误消息,指出它们不存在。阻止错误停止创建,或者异步执行。
Acecool

其他一些注意事项:可以动态创建属性,并以仅针对每个实例存在的方式起作用。您也可以使它们存在,以便它们存在于对象中(多数情况下这是您想要的)。还有的是其中添加的元素是“静止”,即相同的标记,并返回值是所有实例共享情况-如果你在一个区域更新,都得到同样的..
Acecool

-6

提供的很多答案每个属性需要这么多行,即/和/或-由于多个属性需要重复性,我认为这是丑陋或乏味的实现。不能再简化了,或者直到它没有太大用处为止。

简而言之:在完成的作品中,如果我重复两行代码,通常会将其转换为单行辅助函数,以此类推...将数学或奇数参数(例如(start_x,start_y,end_x,end_y)简化为(x,y,w,h)即x,y,x + w,y + h(有时需要min / max或如果w / h为负且实现不喜欢它,我将从x /中减去y和ab w / h等。)。

重写内部getter / setter方法是可行的方法,但是问题是您需要为每个类都这样做,或者将该类作为该基础的父类...这对我不起作用,因为我更希望成为自由选择子代/父代进行继承,子节点等。

我创建了一个解决方案,无需使用Dict数据类型来提供数据即可回答问题,因为我发现输入数据等操作很繁琐,等等。

我的解决方案要求您在类上方添加2行,以为要向其添加属性的类创建基类,然后为每个类添加1行,并且您可以选择添加回调以控制数据,并在数据更改时通知您,限制可以根据值和/或数据类型等进行设置的数据。

您还可以选择使用_object.x,_object.x =值,_object.GetX(),_object.SetX(值),并且它们被等效地处理。

此外,值是分配给类实例的唯一非静态数据,但是实际属性却分配给了类,这意味着您不想重复的事情,不需要重复...可以分配一个默认值,因此每次都有一个不需要的getter,尽管有一个选项可以覆盖默认的默认值,还有另一个选项可以使getter通过覆盖默认的返回值来返回原始存储值(注意:此方法表示仅在分配值时才分配原始值,否则为None-当值重置时,它分配None等。)

辅助函数也有很多-添加的第一个属性将2个左右的辅助函数添加到类中以引用实例值...它们是ResetAccessors(_key,..)varargs重复(所有操作都可以使用第一个命名的args重复进行) )和SetAccessors(_key,_value),还可以选择将更多选项添加到主类中以提高效率-计划的方法是:一种将访问器组合在一起的方式,因此,如果您倾向于每次都重置一些访问器,您可以将它们分配给一个组并重置该组,而不必每次都重复命名的密钥,甚至更多。

实例/原始存储值存储在类中。, 班上。引用访问器类,该类保存该属性的静态vars / values / functions。_类。是属性本身,在设置/获取等过程中通过实例类访问时会调用该属性。

访问器_class .__指向该类,但是由于它是内部的,因此需要在该类中进行分配,这就是为什么我选择使用__Name = AccessorFunc(...)对其进行分配的原因,每个属性一行包含多个可选项要使用的参数(使用键控varargs,是因为它们更容易,更有效地标识和维护)...

如前所述,我还创建了许多函数,其中一些函数使用访问器函数信息,因此不需要调用(因为目前有点不方便-现在您需要使用 _class。.FunctionName(_class_instance ,args)-通过添加运行此马拉松马拉松函数或将访问器添加到对象并使用self(命名为指出它们的方法),我可以使用堆栈/跟踪来获取实例引用以获取值是针对实例的,并保留对自身,AccessorFunc类引用以及函数定义内的其他信息的访问)。

尚未完成,但这是一个了不起的立足点。注意:如果不使用__Name = AccessorFunc(...)创建属性,则即使我在init函数中定义了__键,也无法访问__键。如果这样做,则没有问题。

另外:请注意,名称和键是不同的...名称是“正式的”,在函数名称创建中使用,并且键用于数据存储和访问。即_class.x,其中小写的x是键,名称将是大写的X,因此GetX()是函数,而不是看起来有些奇怪的Getx()。这可以使self.x正常工作并看起来合适,但也可以使GetX()看起来合适。

我有一个示例类,其键/名称相同,但显示不同。为了输出数据而创建了许多辅助函数(注意:并非所有这些都是完整的),因此您可以看到发生了什么。

使用键:x,名称:X的当前功能列表输出为:

这绝不是一个全面的列表-在发布时有一些尚未列入清单...

_instance.SetAccessors( _key, _value [ , _key, _value ] .. )                   Instance Class Helper Function: Allows assigning many keys / values on a single line - useful for initial setup, or to minimize lines.    In short: Calls this.Set<Name>( _value ) for each _key / _value pairing.
_instance.ResetAccessors( _key [ , _key ] .. )                                 Instance Class Helper Function: Allows resetting many key stored values to None on a single line.                                           In short: Calls this.Reset<Name>() for each name provided.


Note: Functions below may list self.Get / Set / Name( _args ) - self is meant as the class instance reference in the cases below - coded as this in AccessorFuncBase Class.

this.GetX( _default_override = None, _ignore_defaults = False )                 GET:            Returns    IF ISSET: STORED_VALUE .. IF IGNORE_DEFAULTS: None  .. IF PROVIDED: DEFAULT_OVERRIDE ELSE: DEFAULT_VALUE       100
this.GetXRaw( )                                                                 RAW:            Returns    STORED_VALUE                                                                                                     100
this.IsXSet( )                                                                  ISSET:          Returns    ( STORED_VALUE != None )                                                                                         True

this.GetXToString( )                                                            GETSTR:         Returns    str( GET )                                                                                                       100
this.GetXLen( _default_override = None, _ignore_defaults = False )              LEN:            Returns    len( GET )                                                                                                       3
this.GetXLenToString( _default_override = None, _ignore_defaults = False )      LENSTR:         Returns    str( len( GET ) )                                                                                                3
this.GetXDefaultValue( )                                                        DEFAULT:        Returns    DEFAULT_VALUE                                                                                                    1111

this.GetXAccessor( )                                                            ACCESSOR:       Returns    ACCESSOR_REF ( self.__<key> )                                                                                    [ AccessorFuncBase ] Key: x : Class ID: 2231452344344 : self ID: 2231448283848        Default: 1111       Allowed Types: {"<class 'int'>": "<class 'type'>", "<class 'float'>": "<class 'type'>"}     Allowed Values: None
this.GetXAllowedTypes( )                                                        ALLOWED_TYPES:  Returns    Allowed Data-Types                                                                                               {"<class 'int'>": "<class 'type'>", "<class 'float'>": "<class 'type'>"}
this.GetXAllowedValues( )                                                       ALLOWED_VALUES: Returns    Allowed Values                                                                                                   None

this.GetXHelpers( )                                                             HELPERS:        Returns    Helper Functions String List - ie what you're reading now...                                                     THESE ROWS OF TEXT
this.GetXKeyOutput( )                                                           Returns information about this Name / Key                                                                                                   ROWS OF TEXT
this.GetXGetterOutput( )                                                        Returns information about this Name / Key                                                                                                   ROWS OF TEXT

this.SetX( _value )                                                             SET:            STORED_VALUE Setter - ie Redirect to __<Key>.Set                                                                            N / A
this.ResetX( )                                                                  RESET:          Resets STORED_VALUE to None                                                                                                 N / A

this.HasXGetterPrefix( )                                                        Returns Whether or Not this key has a Getter Prefix...                                                                                      True
this.GetXGetterPrefix( )                                                        Returns Getter Prefix...                                                                                                                    Get

this.GetXName( )                                                                Returns Accessor Name - Typically Formal / Title-Case                                                                                       X
this.GetXKey( )                                                                 Returns Accessor Property Key - Typically Lower-Case                                                                                        x
this.GetXAccessorKey( )                                                         Returns Accessor Key - This is to access internal functions, and static data...                                                             __x
this.GetXDataKey( )                                                             Returns Accessor Data-Storage Key - This is the location where the class instance value is stored..                                         _x

正在输出的一些数据是:

这是使用Demo类创建的全新类,除了名称_foo(我使用的变量名称)之外,没有分配任何数据(因此可以输出)。

_foo         --- MyClass: ---- id( this.__class__ ): 2231452349064 :::: id( this ): 2231448475016

    Key       Getter Value        | Raw Key   Raw / Stored Value       | Get Default Value             Default Value            | Get Allowed Types             Allowed Types                                                              | Get Allowed Values            Allowed Values                                                                                                                                                                                                                   |

    Name:     _foo                | _Name:    _foo                     | __Name.DefaultValue( ):       AccessorFuncDemoClass    | __Name.GetAllowedTypes( )     <class 'str'>                                                              | __Name.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    x:        1111                | _x:       None                     | __x.DefaultValue( ):          1111                     | __x.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __x.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    y:        2222                | _y:       None                     | __y.DefaultValue( ):          2222                     | __y.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __y.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    z:        3333                | _z:       None                     | __z.DefaultValue( ):          3333                     | __z.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __z.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Blah:     <class 'int'>       | _Blah:    None                     | __Blah.DefaultValue( ):       <class 'int'>            | __Blah.GetAllowedTypes( )     <class 'str'>                                                              | __Blah.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Width:    1                   | _Width:   None                     | __Width.DefaultValue( ):      1                        | __Width.GetAllowedTypes( )    (<class 'int'>, <class 'bool'>)                                            | __Width.GetAllowedValues( )   Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Height:   0                   | _Height:  None                     | __Height.DefaultValue( ):     0                        | __Height.GetAllowedTypes( )   <class 'int'>                                                              | __Height.GetAllowedValues( )  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |
    Depth:    2                   | _Depth:   None                     | __Depth.DefaultValue( ):      2                        | __Depth.GetAllowedTypes( )    Saved Value Restricted to Authorized Values ONLY                           | __Depth.GetAllowedValues( )   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |


this.IsNameSet( ):    True      this.GetName( ):     _foo                     this.GetNameRaw( ):    _foo                     this.GetNameDefaultValue( ):    AccessorFuncDemoClass    this.GetNameLen( ):    4    this.HasNameGetterPrefix( ):    <class 'str'>                                this.GetNameGetterPrefix( ):    None
this.IsXSet( ):       False     this.GetX( ):        1111                     this.GetXRaw( ):       None                     this.GetXDefaultValue( ):       1111                     this.GetXLen( ):       4    this.HasXGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetXGetterPrefix( ):       None
this.IsYSet( ):       False     this.GetY( ):        2222                     this.GetYRaw( ):       None                     this.GetYDefaultValue( ):       2222                     this.GetYLen( ):       4    this.HasYGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetYGetterPrefix( ):       None
this.IsZSet( ):       False     this.GetZ( ):        3333                     this.GetZRaw( ):       None                     this.GetZDefaultValue( ):       3333                     this.GetZLen( ):       4    this.HasZGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetZGetterPrefix( ):       None
this.IsBlahSet( ):    False     this.GetBlah( ):     <class 'int'>            this.GetBlahRaw( ):    None                     this.GetBlahDefaultValue( ):    <class 'int'>            this.GetBlahLen( ):    13   this.HasBlahGetterPrefix( ):    <class 'str'>                                this.GetBlahGetterPrefix( ):    None
this.IsWidthSet( ):   False     this.GetWidth( ):    1                        this.GetWidthRaw( ):   None                     this.GetWidthDefaultValue( ):   1                        this.GetWidthLen( ):   1    this.HasWidthGetterPrefix( ):   (<class 'int'>, <class 'bool'>)              this.GetWidthGetterPrefix( ):   None
this.IsDepthSet( ):   False     this.GetDepth( ):    2                        this.GetDepthRaw( ):   None                     this.GetDepthDefaultValue( ):   2                        this.GetDepthLen( ):   1    this.HasDepthGetterPrefix( ):   None                                         this.GetDepthGetterPrefix( ):   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
this.IsHeightSet( ):  False     this.GetHeight( ):   0                        this.GetHeightRaw( ):  None                     this.GetHeightDefaultValue( ):  0                        this.GetHeightLen( ):  1    this.HasHeightGetterPrefix( ):  <class 'int'>                                this.GetHeightGetterPrefix( ):  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

这是在为所有_foo属性(名称除外)按相同顺序分配以下值之后:'string',1.0,True,9,10,False

this.IsNameSet( ):    True      this.GetName( ):     _foo                     this.GetNameRaw( ):    _foo                     this.GetNameDefaultValue( ):    AccessorFuncDemoClass    this.GetNameLen( ):    4    this.HasNameGetterPrefix( ):    <class 'str'>                                this.GetNameGetterPrefix( ):    None
this.IsXSet( ):       True      this.GetX( ):        10                       this.GetXRaw( ):       10                       this.GetXDefaultValue( ):       1111                     this.GetXLen( ):       2    this.HasXGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetXGetterPrefix( ):       None
this.IsYSet( ):       True      this.GetY( ):        10                       this.GetYRaw( ):       10                       this.GetYDefaultValue( ):       2222                     this.GetYLen( ):       2    this.HasYGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetYGetterPrefix( ):       None
this.IsZSet( ):       True      this.GetZ( ):        10                       this.GetZRaw( ):       10                       this.GetZDefaultValue( ):       3333                     this.GetZLen( ):       2    this.HasZGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetZGetterPrefix( ):       None
this.IsBlahSet( ):    True      this.GetBlah( ):     string Blah              this.GetBlahRaw( ):    string Blah              this.GetBlahDefaultValue( ):    <class 'int'>            this.GetBlahLen( ):    11   this.HasBlahGetterPrefix( ):    <class 'str'>                                this.GetBlahGetterPrefix( ):    None
this.IsWidthSet( ):   True      this.GetWidth( ):    False                    this.GetWidthRaw( ):   False                    this.GetWidthDefaultValue( ):   1                        this.GetWidthLen( ):   5    this.HasWidthGetterPrefix( ):   (<class 'int'>, <class 'bool'>)              this.GetWidthGetterPrefix( ):   None
this.IsDepthSet( ):   True      this.GetDepth( ):    9                        this.GetDepthRaw( ):   9                        this.GetDepthDefaultValue( ):   2                        this.GetDepthLen( ):   1    this.HasDepthGetterPrefix( ):   None                                         this.GetDepthGetterPrefix( ):   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
this.IsHeightSet( ):  True      this.GetHeight( ):   9                        this.GetHeightRaw( ):  9                        this.GetHeightDefaultValue( ):  0                        this.GetHeightLen( ):  1    this.HasHeightGetterPrefix( ):  <class 'int'>                                this.GetHeightGetterPrefix( ):  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

_foo         --- MyClass: ---- id( this.__class__ ): 2231452349064 :::: id( this ): 2231448475016

    Key       Getter Value        | Raw Key   Raw / Stored Value       | Get Default Value             Default Value            | Get Allowed Types             Allowed Types                                                              | Get Allowed Values            Allowed Values                                                                                                                                                                                                                   |

    Name:     _foo                | _Name:    _foo                     | __Name.DefaultValue( ):       AccessorFuncDemoClass    | __Name.GetAllowedTypes( )     <class 'str'>                                                              | __Name.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    x:        10                  | _x:       10                       | __x.DefaultValue( ):          1111                     | __x.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __x.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    y:        10                  | _y:       10                       | __y.DefaultValue( ):          2222                     | __y.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __y.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    z:        10                  | _z:       10                       | __z.DefaultValue( ):          3333                     | __z.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __z.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Blah:     string Blah         | _Blah:    string Blah              | __Blah.DefaultValue( ):       <class 'int'>            | __Blah.GetAllowedTypes( )     <class 'str'>                                                              | __Blah.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Width:    False               | _Width:   False                    | __Width.DefaultValue( ):      1                        | __Width.GetAllowedTypes( )    (<class 'int'>, <class 'bool'>)                                            | __Width.GetAllowedValues( )   Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Height:   9                   | _Height:  9                        | __Height.DefaultValue( ):     0                        | __Height.GetAllowedTypes( )   <class 'int'>                                                              | __Height.GetAllowedValues( )  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |
    Depth:    9                   | _Depth:   9                        | __Depth.DefaultValue( ):      2                        | __Depth.GetAllowedTypes( )    Saved Value Restricted to Authorized Values ONLY                           | __Depth.GetAllowedValues( )   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |

请注意,由于数据类型或值的限制,未分配某些数据-这是设计使然。设置器禁止分配错误的数据类型或值,甚至禁止将其分配为默认值(除非您覆盖默认值保护行为)

该代码尚未发布在这里,因为在示例和说明之后我没有足够的空间...也是因为它将更改。

请注意:在发布时,文件杂乱无章-这将改变。但是,如果您在Sublime Text中运行它并进行编译,或者从Python运行它,它将编译并吐出大量信息-AccessorDB部分未完成(将用于更新Print Getters和GetKeyOutput帮助器)函数以及被更改为Instance函数,可能放在一个函数中并重命名-查找它。

下一步:运行它并不需要所有内容-底部的很多注释内容是用于调试的更多信息-下载时可能不存在。如果是这样,您应该可以取消注释并重新编译以获得更多信息。

我正在寻找一种解决方案,需要MyClassBase:通过,MyClass(MyClassBase):...-如果您知道解决方案,请发布它。

该类中唯一需要的是__行-strinit一样都是用于调试的-可以将它们从Demo类中删除,但是您需要注释掉或删除下面的一些行(_foo / 2/3 )..

顶部的String,Dict和Util类是我的Python库的一部分-它们不完整。我从库中复制了一些我需要的东西,然后创建了一些新东西。完整的代码将链接到完整的库,并将包括完整的库以及提供更新的调用和删除代码(实际上,剩下的唯一代码将是Demo类和print语句-AccessorFunc系统将移至库)。 ..

文件部分:

##
## MyClass Test AccessorFunc Implementation for Dynamic 1-line Parameters
##
class AccessorFuncDemoClassBase( ):
    pass
class AccessorFuncDemoClass( AccessorFuncDemoClassBase ):
    __Name      = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'Name',      default = 'AccessorFuncDemoClass',  allowed_types = ( TYPE_STRING ),                    allowed_values = VALUE_ANY,                 documentation = 'Name Docs',        getter_prefix = 'Get',  key = 'Name',       allow_erroneous_default = False,    options = { } )
    __x         = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'X',         default = 1111,                     allowed_types = ( TYPE_INTEGER, TYPE_FLOAT ),       allowed_values = VALUE_ANY,                 documentation = 'X Docs',           getter_prefix = 'Get',  key = 'x',          allow_erroneous_default = False,    options = { } )
    __Height    = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'Height',    default = 0,                        allowed_types = TYPE_INTEGER,                       allowed_values = VALUE_SINGLE_DIGITS,       documentation = 'Height Docs',      getter_prefix = 'Get',  key = 'Height',     allow_erroneous_default = False,    options = { } )

这种美使得通过AccessorFuncs /回调/数据类型/值强制等动态添加属性来创建新类变得异常容易。

目前,链接位于(此链接应反映对文档的更改。):https : //www.dropbox.com/s/6gzi44i7dh58v61/dynamic_properties_accessorfuncs_and_more.py?dl=0

另外:如果您不使用Sublime Text,我建议在Notepad ++,Atom,Visual Code和其他语言上使用它,因为适当的线程实现使其使用起来非常快得多...我也在研究类似IDE的代码映射系统-请看一下:https : //bitbucket.org/Acecool/acecoolcodemappingsystem/src/master/(首先在软件包管理器中添加Repo,然后再安装插件-在1.0.0版本准备就绪时,我将添加它到主插件列表...)

我希望这个解决方案能对您有所帮助……并且一如既往:

仅仅因为它有效,就不能使它正确-Josh'Acecool'Moser


我想快速显示该类的
外观,

显然,这引起了很多仇恨,这令人困惑。它完全符合OP的要求-动态地向对象添加属性。它还添加了不必包含的辅助函数-也许这就是它变得令人讨厌的原因-并且还确保开发人员可以轻松地访问通过getter处理的属性(.x),存储的原始值(._x)当.x返回默认值或其他内容时可以为None,以及一种访问访问器以使用助手,更改内容等的方式。(.__ x)....
Acecool
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.