【问题标题】:Getting real caller method name when using setattr()使用 setattr() 时获取真正的调用方方法名称
【发布时间】:2019-06-27 10:36:11
【问题描述】:

我有一个简单的类,我想在其中生成基于继承的类字段的方法:

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 模块,但它在功能检查方面似乎有不同的目的。

【问题讨论】:

  • 发布您的toggle_following 声明并描述其目的
  • @RomanPerekhrest 我认为这是minimal reproducible example 的一个很好的例子。实际的切换功能与问题无关,只会使事情复杂化。

标签: python python-3.x metaprogramming getattr setattr


【解决方案1】:

也许这太老套了,也许有更优雅(即专用)的方式来实现这一点,但是:

您可以创建一个包装函数,将 func_name 传递给内部 _toggle func:

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

【讨论】:

  • 谢谢。 :) 整个元编程的想法在设计上是骇人听闻的,所以一切都很好! :D
【解决方案2】:

解决相同问题的另一种方法是按以下方式使用__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

【讨论】:

    猜你喜欢
    • 2013-07-07
    • 2017-04-19
    • 2011-07-03
    • 1970-01-01
    • 1970-01-01
    • 2014-07-11
    • 2011-10-27
    • 1970-01-01
    • 2019-04-20
    相关资源
    最近更新 更多