Python 动态属性和mypy
Python dynamic properties and mypy
我试图将一些函数屏蔽为属性(通过包装器,这在这里并不重要)并将它们动态添加到对象中,但是,我需要代码完成和 mypy 才能工作。
我想出了如何动态添加 属性(通过元类或简单地在构造函数中),但我遇到的问题是 mypy 没有选择它([=21 也没有=]).
一种解决方法是定义一个具有相同 name/type 的属性,但我真的不喜欢这种方法(太多代码、静态属性集、重复)。
有没有更好的方法?
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"
修复您的 IDE? :-)。在 Python 中总会有静态分析无法进行的极端情况。在这种情况下,您获得了本应帮助您实现目标的工具。
没有 运行 代码,IDE 或 Mypy 都无法找到这些动态属性。我知道有 IDEs,至少在过去,采用实际导入模块来自动完成 - 但这也会引发许多附带影响。
我想说您将不得不在没有这些工具的情况下生活,以便拥有您的动态代码 - 甚至可以使用 "don't check this" 标记样式添加注释。根本无法自动完成。
这是我的回复: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
最新的在我的图书馆里,link在最上面的这个文本文件里...
基本上,它可以让你这样做:
##
## 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;
需要 class 首先存在的要求是 meh - 我正在寻找替代方案,但如果我在 init[=38] 期间创建访问器,我已经遇到了问题=] 即使对这些函数的调用总是在 init...之后或 属性 表格..
即:self.p = 1234.03 等同于self.SetP( 1234.03 ), and print( str( self.p ) );与 print( str( self.GetP( ) ) ) 或 print( self.GetPToString( ) ) 等相同...
self.key == 属性
self._key == 存储的原始数据,默认为 None - 默认值存储在 AccessorFunc 对象中,如果原始数据为 [=,则 getter return 为默认值63=],如果只有 getter args 不改变忽略默认值的行为(第二个 arg)... getter 的第一个 arg 是覆盖默认值...
所以 self.GetP( ) 其中 self._p == None 和 self.GetPDefaultValue( ) 是 12.34 将 return 12.34 和 self.GetP ( 24.56 ) 会 return 24.56 而 self.GetP( 'does not matter', True ) 会 return None 因为 ignore_defaults 值(即如果一个值被设置然后它将 return 第二个 arg 设置为 True 的值...但是由于没有设置值,None 是 returned )...
还有数据类型和值保护,因此您可以确保当且仅当(当且仅当)数据类型和/或值被授权时才分配值...如果不是,它被忽略了。
这个系统增加了很多自由度。我很快就会添加更多功能。
如果您看一下我的回复等...您会看到更多即将推出的功能,以及更多...例如:自动设置 init 函数,设置分组系统,所以如果我按顺序用 p、y、r 创建一个组(全部)...然后执行 this.SetAll( p, y, r );那么所有的都将被分配。我需要验证没有命名冲突,但目标是减少需要多少代码。
你可以试试
T = TypeVar('T', bound=Meta)
bar: T = Bar()
somefunc(bar.dynprop)
这不会检查(或抱怨)您的动态属性,但至少它可能知道非动态继承属性。
我试图将一些函数屏蔽为属性(通过包装器,这在这里并不重要)并将它们动态添加到对象中,但是,我需要代码完成和 mypy 才能工作。
我想出了如何动态添加 属性(通过元类或简单地在构造函数中),但我遇到的问题是 mypy 没有选择它([=21 也没有=]).
一种解决方法是定义一个具有相同 name/type 的属性,但我真的不喜欢这种方法(太多代码、静态属性集、重复)。
有没有更好的方法?
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"
修复您的 IDE? :-)。在 Python 中总会有静态分析无法进行的极端情况。在这种情况下,您获得了本应帮助您实现目标的工具。
没有 运行 代码,IDE 或 Mypy 都无法找到这些动态属性。我知道有 IDEs,至少在过去,采用实际导入模块来自动完成 - 但这也会引发许多附带影响。
我想说您将不得不在没有这些工具的情况下生活,以便拥有您的动态代码 - 甚至可以使用 "don't check this" 标记样式添加注释。根本无法自动完成。
这是我的回复: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
最新的在我的图书馆里,link在最上面的这个文本文件里...
基本上,它可以让你这样做:
##
## 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;
需要 class 首先存在的要求是 meh - 我正在寻找替代方案,但如果我在 init[=38] 期间创建访问器,我已经遇到了问题=] 即使对这些函数的调用总是在 init...之后或 属性 表格..
即:self.p = 1234.03 等同于self.SetP( 1234.03 ), and print( str( self.p ) );与 print( str( self.GetP( ) ) ) 或 print( self.GetPToString( ) ) 等相同...
self.key == 属性 self._key == 存储的原始数据,默认为 None - 默认值存储在 AccessorFunc 对象中,如果原始数据为 [=,则 getter return 为默认值63=],如果只有 getter args 不改变忽略默认值的行为(第二个 arg)... getter 的第一个 arg 是覆盖默认值...
所以 self.GetP( ) 其中 self._p == None 和 self.GetPDefaultValue( ) 是 12.34 将 return 12.34 和 self.GetP ( 24.56 ) 会 return 24.56 而 self.GetP( 'does not matter', True ) 会 return None 因为 ignore_defaults 值(即如果一个值被设置然后它将 return 第二个 arg 设置为 True 的值...但是由于没有设置值,None 是 returned )...
还有数据类型和值保护,因此您可以确保当且仅当(当且仅当)数据类型和/或值被授权时才分配值...如果不是,它被忽略了。
这个系统增加了很多自由度。我很快就会添加更多功能。
如果您看一下我的回复等...您会看到更多即将推出的功能,以及更多...例如:自动设置 init 函数,设置分组系统,所以如果我按顺序用 p、y、r 创建一个组(全部)...然后执行 this.SetAll( p, y, r );那么所有的都将被分配。我需要验证没有命名冲突,但目标是减少需要多少代码。
你可以试试
T = TypeVar('T', bound=Meta)
bar: T = Bar()
somefunc(bar.dynprop)
这不会检查(或抱怨)您的动态属性,但至少它可能知道非动态继承属性。