6

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

这是一个例子:

我有一Filter堂课:

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)

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

当我运行这个:

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

预期的:

  • linux
  • con(应该是动态设置属性的名称)

我得到:

  • linux
  • self.condition条件(属性值的名称(未绑定))

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

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

谢谢。

4

2 回答 2

1

function.__name__是函数最初定义的名称,它与访问它的名称无关。实际上,关键function.__name__在于正确识别函数,无论使用什么名称来访问它。你肯定想阅读这篇文章以了解更多关于 Python 的“名称”是什么

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)

作为旁注(完全不相关,但我认为您可能想知道这一点),使用可变对象作为函数参数默认值是最臭名昭著的 Python 陷阱之一,并且可能会产生完全意想不到的结果,即:

>>> 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']

编辑:

谢谢你的回答,但这并没有触及我的问题。我的问题不是函数是如何命名或定义的,我的问题是当我使用 setattr() 并设置一个属性并给它一个函数作为它的值时,我可以访问该值并执行该值的作用,但是因为它是一个函数,为什么它不返回它的名字作为函数名

因为正如我上面已经解释的那样,函数的__name__属性和引用该函数的Filter实例属性的名称是完全不相关的,并且该函数对引用它的变量或属性的名称一无所知,如参考中所述我链接到的文章。

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

于 2019-03-19T13:34:34.617 回答
0

我可以建议你这个:

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 的评论后编辑。]

于 2019-03-19T13:30:28.717 回答