使用 setattr() 时获取真正的调用方方法名称
Getting real caller method name when using setattr()
我有一个简单的 class,我想在其中生成基于继承的 class 字段的方法:
class Parent:
def __init__(self, *args, **kwargs):
self.fields = getattr(self, 'TOGGLEABLE')
self.generate_methods()
def _toggle(self, instance):
print(self, instance) # Prints correctly
# Here I need to have the caller method, which is:
# toggle_following()
def generate_methods(self):
for field_name in self.fields:
setattr(self, f'toggle_{field_name}', self._toggle)
class Child(Parent):
following = ['a', 'b', 'c']
TOGGLEABLE = ('following',)
此时Child
中有一个toggle_following
函数成功生成。
然后我用一个参数调用它:
>>> child = Child()
>>> child.toggle_following('b')
<__main__.Child object at 0x104d21b70> b
并在 print
语句中打印出预期结果。
但我需要在我的通用 _toggle
函数中接收调用者姓名 toggle_following
。
我试过使用 inspect
模块,但它在功能检查方面似乎有不同的用途。
也许这太老套了,也许有更优雅(即专用)的方式来实现这一点,但是:
您可以创建一个包装函数,将 func_name 传递给内部 _toggle
函数:
class Parent:
def __init__(self, *args, **kwargs):
self.fields = getattr(self, 'TOGGLEABLE')
self.generate_methods()
def _toggle(self, instance, func_name):
print(self, instance) # Prints correctly
print(func_name)
def generate_methods(self):
for field_name in self.fields:
func_name = 'toggle_{}'.format(field_name) # I don't have python 3.7 on this computer :P
setattr(self, func_name, lambda instance: self._toggle(instance, func_name))
class Child(Parent):
following = ['a', 'b', 'c']
TOGGLEABLE = ('following',)
child = Child()
child.toggle_following('b')
输出:
<__main__.Child object at 0x7fbe566c0748> b
toggle_following
另一种解决相同问题的方法是按以下方式使用 __getattr__
:
class Parent:
def _toggle(self, instance, func_name):
print(self, instance) # Prints correctly
print(func_name)
def __getattr__(self, attr):
if not attr.startswith("toggle_"):
raise AttributeError("Attribute {} not found".format(attr))
tmp = attr.replace("toggle_", "")
if tmp not in self.TOGGLEABLE:
raise AttributeError(
"You cannot toggle the untoggleable {}".format(attr)
)
return lambda x: self._toggle(x, attr)
class Child(Parent):
following = ['a', 'b', 'c']
TOGGLEABLE = ('following',)
child = Child()
# This can toggle
child.toggle_following('b')
# This cannot toggle
child.toggle_something('b')
产生:
(<__main__.Child instance at 0x107a0e290>, 'b')
toggle_following
Traceback (most recent call last):
File "/Users/urban/tmp/test.py", line 26, in <module>
child.toggle_something('b')
File "/Users/urban/tmp/test.py", line 13, in __getattr__
raise AttributeError("You cannot toggle the untoggleable {}".format(attr))
AttributeError: You cannot toggle the untoggleable toggle_something
我有一个简单的 class,我想在其中生成基于继承的 class 字段的方法:
class Parent:
def __init__(self, *args, **kwargs):
self.fields = getattr(self, 'TOGGLEABLE')
self.generate_methods()
def _toggle(self, instance):
print(self, instance) # Prints correctly
# Here I need to have the caller method, which is:
# toggle_following()
def generate_methods(self):
for field_name in self.fields:
setattr(self, f'toggle_{field_name}', self._toggle)
class Child(Parent):
following = ['a', 'b', 'c']
TOGGLEABLE = ('following',)
此时Child
中有一个toggle_following
函数成功生成。
然后我用一个参数调用它:
>>> child = Child()
>>> child.toggle_following('b')
<__main__.Child object at 0x104d21b70> b
并在 print
语句中打印出预期结果。
但我需要在我的通用 _toggle
函数中接收调用者姓名 toggle_following
。
我试过使用 inspect
模块,但它在功能检查方面似乎有不同的用途。
也许这太老套了,也许有更优雅(即专用)的方式来实现这一点,但是:
您可以创建一个包装函数,将 func_name 传递给内部 _toggle
函数:
class Parent:
def __init__(self, *args, **kwargs):
self.fields = getattr(self, 'TOGGLEABLE')
self.generate_methods()
def _toggle(self, instance, func_name):
print(self, instance) # Prints correctly
print(func_name)
def generate_methods(self):
for field_name in self.fields:
func_name = 'toggle_{}'.format(field_name) # I don't have python 3.7 on this computer :P
setattr(self, func_name, lambda instance: self._toggle(instance, func_name))
class Child(Parent):
following = ['a', 'b', 'c']
TOGGLEABLE = ('following',)
child = Child()
child.toggle_following('b')
输出:
<__main__.Child object at 0x7fbe566c0748> b
toggle_following
另一种解决相同问题的方法是按以下方式使用 __getattr__
:
class Parent:
def _toggle(self, instance, func_name):
print(self, instance) # Prints correctly
print(func_name)
def __getattr__(self, attr):
if not attr.startswith("toggle_"):
raise AttributeError("Attribute {} not found".format(attr))
tmp = attr.replace("toggle_", "")
if tmp not in self.TOGGLEABLE:
raise AttributeError(
"You cannot toggle the untoggleable {}".format(attr)
)
return lambda x: self._toggle(x, attr)
class Child(Parent):
following = ['a', 'b', 'c']
TOGGLEABLE = ('following',)
child = Child()
# This can toggle
child.toggle_following('b')
# This cannot toggle
child.toggle_something('b')
产生:
(<__main__.Child instance at 0x107a0e290>, 'b')
toggle_following
Traceback (most recent call last):
File "/Users/urban/tmp/test.py", line 26, in <module>
child.toggle_something('b')
File "/Users/urban/tmp/test.py", line 13, in __getattr__
raise AttributeError("You cannot toggle the untoggleable {}".format(attr))
AttributeError: You cannot toggle the untoggleable toggle_something