269

目标是创建一个行为类似于 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返回一个属性对象。

用替换setattr线k = property(lambda x: vs[i])根本没有用。

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

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

4

25 回答 25

403

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

可以动态地将属性添加到类中。但这就是问题所在:您必须将其添加到class中。

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

Aproperty实际上是一个叫做描述符的东西的简单实现。它是一个在给定类上为给定属性提供自定义处理的对象。有点像if__getattribute__.

当我foo.b在上面的示例中询问时,Python 看到b类上的定义实现了描述符协议——这只是意味着它是一个带有__get____set____delete__方法的对象。描述符声称负责处理该属性,因此 Python 调用Foo.b.__get__(foo, Foo),并将返回值作为属性的值传回给您。在 的情况下property,这些方法中的每一个都只调用fget, fset,或者fdel您传递给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.bar仍然调用property.__get__;当作为类属性访问时,描述符返回自己是惯用的。

我认为几乎所有 Python 的 OO 系统都可以用 Python 来表达,这很酷。:)

哦,如果你有兴趣,我不久前写了一篇关于描述符的冗长博文。

于 2009-08-31T01:30:05.797 回答
64

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

那么你想要的是一本可以将 a['b'] 拼写为 ab 的字典吗?

这很容易:

class atdict(dict):
    __getattr__= dict.__getitem__
    __setattr__= dict.__setitem__
    __delattr__= dict.__delitem__
于 2009-08-25T14:33:07.500 回答
41

您不需要为此使用属性。只需覆盖__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!
于 2009-08-25T02:41:46.710 回答
40

似乎您可以使用 更简单地解决此问题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()不适用于实例。

于 2009-08-25T02:28:05.873 回答
13

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

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

创建一个类

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

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

o = C()

在 Python 中,我们期望有一种明显的做事方式。但是,在这种情况下,我将展示两种方式:使用装饰器表示法和不使用。首先,没有装饰器符号。这对于 getter、setter 或 deleter 的动态分配可能更有用。

动态(又名猴子补丁)

让我们为我们的类创建一些:

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
于 2015-02-05T23:12:19.850 回答
9

这是一个解决方案:

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

定义类后,您只需执行以下操作以动态添加属性:

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)
于 2019-09-27T19:31:05.033 回答
6

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

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__仅在需要该值时才做某事是好的,例如查询数据库。set/get 组合可以很好地简化对存储在类中的数据的访问(如上例所示)。

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

于 2016-08-10T12:58:15.457 回答
5

不确定我是否完全理解这个问题,但您可以在运行时使用__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
于 2009-08-25T02:19:32.473 回答
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)
>>>

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

于 2009-08-26T09:15:12.857 回答
5

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

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
于 2018-02-27T20:29:51.150 回答
4

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

于 2009-08-25T02:23:36.490 回答
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'

于 2009-08-25T03:01:35.260 回答
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
于 2014-05-09T21:41:06.130 回答
2

这与 OP 想要的有点不同,但我的大脑一直在颤抖,直到我找到一个可行的解决方案,所以我在这里为下一个家伙/gal

我需要一种方法来指定动态设置器和获取器。

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

是否令人困惑?是的,很抱歉,我无法提出任何有意义的现实世界示例。此外,这不适合轻松愉快的人。

于 2019-07-31T20:32:24.180 回答
1

尽管给出了很多答案,但我找不到一个我满意的答案。我想出了我自己的解决方案,它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
于 2019-02-23T05:54:02.240 回答
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__)
于 2009-08-26T08:50:30.597 回答
0

要回答您问题的主旨,您需要一个来自 dict 的只读属性作为不可变数据源:

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

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

>>> dummy.ab
100

我将演示如何使用模块中的 anamedtuplecollections完成此操作:

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

于 2015-02-06T02:04:54.683 回答
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
于 2016-11-22T14:38:58.803 回答
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>
于 2019-02-13T09:56:15.677 回答
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
于 2019-05-26T21:16:25.837 回答
0

如果要求是基于某些实例属性动态生成属性,那么下面的代码可能很有用:

import random  

class Foo:
    def __init__(self, prop_names: List[str], should_property_be_zero: bool = False) -> None:
        self.prop_names = prop_names
        self.should_property_be_zero = should_property_be_zero
        
    def create_properties(self):
        for name in self.prop_names:
            setattr(self.__class__, name, property(fget=lambda self: 0 if self.should_property_be_zero else random.randint(1, 100)))

需要注意的重要一点是使用setattr(self.__class__, name, ...)而不是setattr(self, name, ...)

使用示例:

In [261]: prop_names = ['a', 'b']

In [262]: ff = Foo(prop_names=prop_names, should_property_be_zero=False)

In [263]: ff.create_properties()

In [264]: ff.a
Out[264]: 10

In [265]: ff.b
Out[265]: 37

In [266]: ft = Foo(prop_names=prop_names, should_property_be_zero=True)

In [267]: ft.create_properties()

In [268]: ft.a
Out[268]: 0

In [269]: ft.b
Out[269]: 0

设置属性将按AttributeError: can't set attribute预期提高:

In [270]: ff.a = 5
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-270-5f9cad5b617d> in <module>
----> 1 ff.a = 5

AttributeError: can't set attribute

In [271]: ft.a = 5
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-271-65e7b8e25b67> in <module>
----> 1 ft.a = 5

AttributeError: can't set attribute
于 2021-12-01T12:41:37.803 回答
-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
于 2014-04-29T17:52:55.890 回答
-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
'''
于 2020-01-05T20:03:25.890 回答
-2

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

class Holder: p = property(lambda x: vs[i], self.fn_readonly)
setattr(self, k, Holder().p)
于 2009-08-25T02:29:19.117 回答
-6

许多提供的答案需要每个属性这么多行,即 / 和 / 或 - 由于多个属性需要重复性等,我认为这是一个丑陋或乏味的实现。我更喜欢保持精简/简化它们直到它们不能再简化,或者直到它没有太多的目的。

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

覆盖内部 getter / setter 是一种不错的方法,但问题是您需要为每个类都这样做,或者将类作为该基础的父类......这对我不起作用,因为我更愿意成为自由选择子/父进行继承、子节点等。

我创建了一个解决方案,可以在不使用 Dict 数据类型来提供数据的情况下回答问题,因为我发现输入数据等很乏味......

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

您还可以选择使用 _object.x、_object.x = value、_object.GetX( )、_object.SetX( value ),它们的处理方式相同。

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

也有许多辅助函数 - 添加的第一个属性向类添加 2 个左右的辅助函数以引用实例值...它们是 ResetAccessors(_key, ..) 可变参数重复(所有都可以使用第一个命名参数重复) 和 SetAccessors( _key, _value ) 可以选择在主类中添加更多以帮助提高效率 - 计划的方法是:一种将访问器组合在一起的方法,因此如果您倾向于一次重置几个访问器,则每次,您可以将它们分配给一个组并重置该组,而不是每次都重复命名键等等。

实例/原始存储值存储在类中。, 班上。引用包含属性的静态变量/值/函数的访问器类。_班级。是在设置/获取等期间通过实例类访问时调用的属性本身。

Accessor _class.__ 指向该类,但由于它是内部的,因此需要在类中分配,这就是为什么我选择使用 __Name = AccessorFunc( ... ) 来分配它,每个属性一行有许多可选要使用的参数(使用键控可变参数,因为它们更容易识别和维护)......

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

它还没有完成,但它是一个很棒的立足点。注意:如果您不使用 __Name = AccessorFunc( ... ) 创建属性,您将无法访问 __ 键,即使我在 init 函数中定义了它。如果你这样做了,那么就没有问题了。

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

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

当前使用 key: x, name: 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 属性(除了 name )以下值之后:'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)                                                                                                                                                                                                   |

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

代码没有在这里发布,因为在示例和解释之后我没有空间......也因为它会改变。

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

下一篇:运行它并不是所有的东西都需要——底部的许多注释内容是为了提供更多用于调试的信息——下载它时它可能不存在。如果是,您应该能够取消注释并重新编译以获取更多信息。

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

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

顶部的 String、Dict 和 Util 类是我的 Python 库的一部分——它们并不完整。我从图书馆复制了一些我需要的东西,并创建了一些新的东西。完整的代码将链接到完整的库,并将包含它以及提供更新的调用和删除代码(实际上,剩下的唯一代码将是演示类和打印语句 - 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

于 2018-08-31T08:20:53.847 回答