[英]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
構造函數的fget
、 fset
或fdel
。
描述符實際上是 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
這是一個解決方案:
定義類后,您只需執行以下操作即可動態添加屬性:
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
模塊中的namedtuple
元collections
來完成此操作:
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 ): ... - 如果你知道一個解決方案 - 發布它。
類中唯一需要的是 __ 行 - 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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.