我试图将一些函数作为属性进行掩码(通过这里不重要的包装器)并将它们动态添加到对象中,但是,我需要代码完成和mypy才能工作。
我想出了如何动态添加属性(通过元类或只是在构造函数中),但我遇到的问题是mypy没有拿起它(IDE也没有)。
一种解决方法是定义具有相同名称/类型的属性,但我真的不喜欢这种方法(代码太多,静态属性集,重复)。
有更好的方法吗?
class Meta(type):
def __new__(cls, clsname, bases, dct):
def prop(self) -> int:
return 1
inst = super(Meta, cls).__new__(cls, clsname, bases, dct)
inst.dynprop=property(prop)
return inst
class Foo(metaclass=Meta):
dynprop=int #this works, but I don't want it
class Bar(metaclass=Meta):pass
def somefunc(s:str):
print(s)
foo=Foo()
bar=Bar()
somefunc(foo.dynprop) #this is ok
somefunc(bar.dynprop) #meta.py:24: error: "Bar" has no attribute "dynprop"
答案 0 :(得分:1)
修复你的IDE? :-)。在Python中,总会出现静态分析无法进行的极端情况。在这种情况下,你得到的工具应该可以帮助你阻止你。
如果不运行代码,IDE或Mypy无法找到这些动态属性。我知道有IDE,至少在过去,使用实际导入模块进行自动完成 - 但这也可以触发大量的附带效果。
我会说你必须没有这些工具才能拥有动态代码 - 用“不检查这个”标记样式添加注释。自动完成功能根本不可能。
答案 1 :(得分:0)
这是我的回复:https://www.dropbox.com/s/skj81l6upddrqpy/dynamic_properties_information.txt?dl=0
这是我在Python中为动态AccessorFuncs / Properties实现的新版本的旧版本:https://www.dropbox.com/s/6gzi44i7dh58v61/dynamic_properties_accessorfuncs_and_more.py?dl=0
最新消息在我的库中,链接在此文本文件的最上方...
基本上,您可以这样做:
##
## Angle Base Class - Note: I could parent the same way Vector does and change the getattr magic function to alter x / y / z to p / y / r for pitch, yaw and roll without re-declaring anything...
##
class AngleBase( Pos ):
pass;
class Angle( AngleBase ):
##
__name__ = 'Angle';
## Note, I'm using self.p / y / r for to string data instead of functions because if I rename the property from x, y, z dynamically without re-declaring then it means I'd either need to rename all of the functions too, or just re-declare, or simply use self.p / y / r instead, everywhere...
## Task: Add system to rename functions in this regard to allow prevention of adding so much duplicate code...
__serialize = AccessorFuncBase( parent = AngleBase, key = 'serialize', name = 'Serialize', default = 'Angle( 0.0, 0.0, 0.0 );', getter_prefix = '', documentation = 'Serialize Data', allowed_types = ( TYPE_STRING ), allowed_values = ( VALUE_ANY ), setup = { 'get': ( lambda this: 'Angle( ' + str( this.p ) + ', ' + str( this.y ) + ', ' + str( this.r ) + ' );' ) } );
## Note: I could set up pitch, yaw, roll with Get / Set redirecting to p / y / r.... This would make __pitch, __yaw, and __roll available... But I don't want to override pitch / yaw / roll / _pitch / _yaw / _roll created by these 3 aliases... So I'll likely just need to add the alias system for names too.. Honestly, I should change the defaults to Pitch / Yaw / Roll and add P / Y / R as the aliases..
__p = AccessorFuncBase( parent = AngleBase, key = 'p', keys = [ 'pitch' ], name = 'Pitch', names = [ 'P' ], default = 0.0, getter_prefix = 'Get', documentation = 'Pitch', allowed_types = ( TYPE_INTEGER, TYPE_FLOAT ), allowed_values = ( VALUE_ANY ) );
__y = AccessorFuncBase( parent = AngleBase, key = 'y', keys = [ 'yaw' ], name = 'Yaw', names = [ 'Y' ], default = 0.0, getter_prefix = 'Get', documentation = 'Yaw', allowed_types = ( TYPE_INTEGER, TYPE_FLOAT ), allowed_values = ( VALUE_ANY ) );
__r = AccessorFuncBase( parent = AngleBase, key = 'r', keys = [ 'roll' ], name = 'Roll', names = [ 'R' ], default = 0.0, getter_prefix = 'Get', documentation = 'Roll', allowed_types = ( TYPE_INTEGER, TYPE_FLOAT ), allowed_values = ( VALUE_ANY ) );
##
## This isn't necessary... As the defaults are already 0.0, using the getter or the property will return that value... This is a convenience function to allow assigning all values at once...
##
def __init__( self, _pitch = 0.0, _yaw = 0.0, _roll = 0.0 ):
## Update all of the properties - Note: I could use self.SetPitch( _pitch ), self._p = _pitch, and a few other options. I'm using short-hand here for sake of efficiency... But self.SetPitch( ... ) is actually called when self.p / self.pitch is reassigned with _pitch...
self.p = _pitch;
self.y = _yaw;
self.r = _roll;
首先需要让类存在的要求是meh-我正在寻找一种替代方法,但是即使我在 init 期间创建访问器,也已经遇到了问题,即使对这些函数的调用总是在 init 之后。...因此,通过这种方式,我保留了带有其他选项的完整动态功能,并且可以使用函数形式或属性形式。
即:self.p = 1234.03与self.SetP(1234.03)和print(str(self.p))相同;与print(str(self.GetP()))或print(self.GetPToString())等相同...
self.key ==属性 self._key ==存储的原始数据,默认为None-默认值存储在AccessorFunc对象中,如果raw为none,则getter返回默认值,如果有,则仅当getter args不改变要忽略的行为时默认值(第二个arg)...吸气剂的第一个参数是覆盖默认值...
因此self.GetP(),其中self._p ==无,self.GetPDefaultValue()为12.34将返回12.34,self.GetP(24.56)将返回24.56,self.GetP('无所谓',True)会由于ignore_defaults值而返回None(即,如果设置了一个值,则它将返回第二个arg设置为True的值...但是由于未设置任何值,因此返回None)...
还有数据类型和值保护,因此您可以确保分配给值的是iff(仅当且仅当)数据类型和/或值才被授权...如果不是,则为忽略了。
此系统增加了很多自由。我将很快添加更多功能。
如果您查看我的回复等。。您会看到更大的功能列表,还有更多...例如:自动设置 init 功能,设置设置分组系统,以便如果我按该顺序创建一个具有p,y,r的组(全部)...则执行此操作。SetAll(p,y,r);那么我将需要验证所有命名冲突,但目的是减少所需的代码量。
答案 2 :(得分:0)
您可以尝试类似的
T = TypeVar('T', bound=Meta)
bar: T = Bar()
somefunc(bar.dynprop)
这不会检查(或抱怨)您的动态属性,但至少它 可以知道非动态继承的属性。