簡體   English   中英

如何動態地向類添加屬性?

[英]How to add property to a class dynamically?

目標是創建一個模擬類,其行為類似於 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返回一個屬性對象。

k = property(lambda x: vs[i])替換setattr行根本沒有用。

那么在運行時創建實例屬性的正確方法是什么?

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

我想我應該擴大這個答案,現在我年紀大了,更聰明了,知道發生了什么。 遲到總比不到好。

可以動態地向類添加屬性。 但這就是問題所在:您必須將其添加到class 中

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

property實際上是稱為描述符的事物的簡單實現。 它是一個對象,可為給定類上的給定屬性提供自定義處理。 有點像從__getattribute__出一個巨大的if樹的方法。

當我在上面的例子中請求foo.b時,Python 看到在類上定義的b實現了描述符協議——這只是意味着它是一個具有__get____set____delete__方法的對象。 描述符聲稱負責處理該屬性,因此 Python 調用Foo.b.__get__(foo, Foo) ,並將返回值作為屬性值傳回給您。 property的情況下,這些方法中的每一個fset調用傳遞給property構造函數的fgetfsetfdel

描述符實際上是 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 表示非常酷。 :)

哦,如果您有興趣,我不久前寫了一篇關於描述符冗長博客文章

目標是創建一個模擬類,其行為類似於 db 結果集。

那么你想要的是一本可以將 a['b'] 拼寫為 ab 的字典?

這很容易:

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

似乎您可以使用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()不適用於實例。

您不需要為此使用屬性。 只需覆蓋__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!

如何動態地向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

這是一個解決方案:

  • 允許將屬性名稱指定為 strings ,因此它們可以來自某些外部數據源,而不是全部列在您的程序中。
  • 在定義類時添加屬性,而不是每次創建對象時添加

定義類后,您只需執行以下操作即可動態添加屬性:

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)

對於那些來自搜索引擎的人,這里是我在談論動態屬性時要尋找的兩件事:

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()內置函數。

不確定我是否完全理解這個問題,但是您可以在運行時使用類的內置__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

在這篇 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)
>>>

您可以使用它的一些變體來創建默認值,這是您的目標(該問題中也有一個解決此問題的答案)。

您可以使用以下代碼使用字典對象更新類屬性:

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

您不能在運行時向實例添加新的property() ,因為屬性是數據描述符。 相反,您必須動態創建一個新類或重載__getattribute__以處理實例上的數據描述符。

實現的最佳方法是定義__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'

另一個例子如何達到預期的效果

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

這與 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

是否令人困惑? 是的,抱歉,我無法想出任何有意義的現實世界示例。 此外,這不適合輕松的人。

雖然給出了很多答案,但我找不到一個我滿意的。 我想出了自己的解決方案,使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

這似乎有效(但見下文):

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__)

要回答您的問題的主要內容,您需要 dict 中的只讀屬性作為不可變數據源:

目標是創建一個模擬類,其行為類似於 db 結果集。

因此,例如,如果使用 dict 表達式{'ab':100, 'cd':200}返回數據庫查詢,那么我會看到

>>> dummy.ab 100

我將演示如何使用collections模塊中的namedtuplecollections來完成此操作:

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

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

擴展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>

對我有用的是這樣的:

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

如果需要基於某個實例屬性動態生成屬性,那么下面的代碼可能很有用:

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 as expected:

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

我最近遇到了類似的問題,我想出的解決方案使用__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

這是以編程方式創建屬性對象的簡單示例。

#!/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
'''

動態附加屬性的唯一方法是使用新屬性創建一個新類及其實例。

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

許多提供的答案需要每個屬性這么多行,即 / 和 / 或 - 由於多個屬性需要重復性等,我認為這是一個丑陋或乏味的實現。我更喜歡保持沸騰/簡化它們直到它們不能再簡化,或者直到它沒有多大用處。

簡而言之:在已完成的作品中,如果我重復 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, .. ) varargs 重復(所有可以使用第一個命名的 args 重復) 和 SetAccessors( _key, _value ) 和更多被添加到主類以幫助提高效率的選項 - 計划是:一種將訪問器組合在一起的方法,所以如果你傾向於一次重置幾個,每次,您可以將它們分配到一個組並重置該組,而不是每次都重復命名的鍵,等等。

實例/原始存儲值存儲在類中。 , 班上。 引用包含屬性的靜態變量/值/函數的訪問器類。 _班級。 是在設置/獲取等過程中通過實例類訪問時調用的屬性本身。

Accessor _class.__ 指向類,但因為它是內部的,所以需要在類中分配,這就是為什么我選擇使用 __Name = AccessorFunc( ... ) 來分配它,每個屬性一行,有許多可選要使用的參數(使用帶鍵的可變參數,因為它們更容易識別和更有效地識別和維護)...

我還創建了很多函數,如上所述,其中一些使用訪問器函數信息,因此不需要調用(因為目前有點不方便 - 現在您需要使用_class..FunctionName( _class_instance , args ) - 我繞過使用堆棧/跟蹤來獲取實例引用來獲取值,方法是添加運行此位馬拉松的函數,或者通過將訪問器添加到對象並使用 self (命名為 this 以指出它們'用於實例並保留對 self、AccessorFunc 類引用和函數定義中的其他信息的訪問權限)。

它還沒有完全完成,但它是一個夢幻般的立足點。 注意:如果您不使用 __Name = AccessorFunc( ... ) 創建屬性,即使我在 init 函數中定義了 __ 鍵,您也將無法訪問它。 如果你這樣做,那么就沒有問題。

另外:注意Name和Key是不同的... Name是'正式',用於Function Name Creation,key是用於數據存儲和訪問。 即 _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 屬性(名稱除外)分配以下值之后:'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 helper函數以及被更改為實例函數,可能放入單個函數並重命名 - 尋找它..)

下一頁: 並非所有內容都需要它才能運行 - 底部的許多注釋內容都是用於調試的更多信息 - 當您下載它時,它可能不在那里。 如果是,您應該能夠取消注釋並重新編譯以獲取更多信息。

我正在尋找一種解決方法來需要 MyClassBase: pass, MyClass( MyClassBase ): ... - 如果你知道一個解決方案 - 發布它。

類中唯一需要的是 __ 行 - strinit一樣用於調試 - 它們可以從演示類中刪除,但您需要注釋掉或刪除下面的一些行( _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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM