Python 函数的 setattr() 采用初始函数名称

Python setattr() to function takes initial function name

我确实理解 setattr() 在 python 中的工作原理,但我的问题是当我尝试动态设置属性并将未绑定函数作为值赋予它时,因此该属性是可调用的,当我调用 attr.__name__ 而不是属性名称时,属性最终采用未绑定函数的名称。

这是一个例子:

我有一个 Filter class:

class Filter:
    def __init__(self, column=['poi_id', 'tp.event'], access=['con', 'don']):
        self.column = column
        self.access = access
        self.accessor_column = dict(zip(self.access, self.column))
        self.set_conditions()

    def condition(self, name):
        # i want to be able to get the name of the dynamically set 
        # function and check `self.accessor_column` for a value, but when
        # i do `setattr(self, 'accessor', self.condition)`, the function 
        # name is always set to `condition` rather than `accessor`
        return name

    def set_conditions(self):
        mapping = list(zip(self.column, self.access))
        for i in mapping:
            poi_column = i[0]
            accessor = i[1]
            setattr(self, accessor, self.condition)

在上面的 class 中,set_conditions 函数动态设置过滤器 class 的属性(condon)并为它们分配一个可调用对象, 但它们保留了函数的初始名称。

当我 运行 这个:

>>> f = Filter()
>>> print(f.con('linux'))
>>> print(f.con.__name__)

预计:

我得到:

但我希望 f.con.__name__ 到 return 属性的名称 (con) 而不是分配给它的未绑定函数的名称 (condition)。

有人可以向我解释为什么会出现这种行为吗?我该如何解决?

谢谢。

我可以向你推荐这个吗:

from types import SimpleNamespace

class Filter:
    def __init__(self, column=['poi_id', 'tp.event'], access=['con', 'don']):
        self.column = column
        self.access = access
        self.accessor_column = dict(zip(self.access, self.column))
        self.set_conditions()

    def set_conditions(self):
        for i in self.access:
            setattr(self, i, SimpleNamespace(name=i, func=lambda name: name))

f = Filter()
print(f.con.func('linux'))
>>> linux
print(f.con.name)
>>> con

[根据 bruno desthuilliers 的评论进行编辑。]

function.__name__是函数最初定义的名称,与访问时的名称无关。实际上,function.__name__ 的全部意义在于正确识别用于访问它的任何名称的函数。你肯定想要 read this for more on what Python's "names" are.

此处可能的解决方案之一是用闭包替换 condition 的静态定义:

class Filter(object):
    def __init__(self, column=['poi_id', 'tp.event'], access=['con', 'don']):
        self.column = column
        self.access = access
        self.accessor_column = dict(zip(self.access, self.column))
        self.set_conditions()

    def set_conditions(self):
        mapping = list(zip(self.column, self.access))
        for column_name, accessor_name in mapping:
            def accessor(name):
                print("in {}.accessor '{}' for column '{}'".format(self, accessor_name, column_name))
                return name

            # this is now technically useless but helps with inspection
            accessor.__name__ = accessor_name
            setattr(self, accessor_name, accessor)

作为旁注(完全不相关,但我想你可能想知道这一点),使用可变对象作为函数参数默认是 one of the most infamous Python gotchas 并且可能会产生完全意想不到的结果,即:

>>> f1 = Filter()
>>> f2 = Filter()
>>> f1.column
['poi_id', 'tp.event']
>>> f2.column
['poi_id', 'tp.event']
>>> f2.column.append("WTF")
>>> f1.column
['poi_id', 'tp.event', 'WTF']

编辑:

thank you for your answer, but it doesn't touch my issue here. My problem is not how functions are named or defined, my problem it that when i use setattr() and i set an attribute and i give it a function as it's value, i can access the value and perform what the value does, but since it's a function, why doesn't it return it's name as the function name

因为正如我上面已经解释的那样,函数的 __name__ 属性和引用此函数的 Filter 实例属性的名称完全无关,函数绝对知道 没有关于引用它的变量或属性的名称,如我链接到的参考文章中所述。

实际上,您传递给 setattr 的对象是一个函数这一事实完全无关紧要,从对象的 POV 来看,它只是一个名称和一个对象,句号。事实上,你正在将这个对象(函数或只是 whatever 对象)绑定到一个实例属性(无论是直接还是使用 setattr(),它的工作原理都是一样的)而不是一个普通变量也完全不相关——none 这些操作将对绑定的对象产生任何影响(除了增加它的引用计数器,但这是一个 CPython 实现细节——其他实现可能会以不同的方式实现垃圾收集)。