Python setattr () для функции принимает начальное имя функции - PullRequest
3 голосов
/ 19 марта 2019

Я понимаю, как 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 динамически устанавливает атрибуты (con и don) класса Filter и назначает их для вызова, но они сохраняют первоначальное имя функции.

Когда я запускаю это:

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

Ожидаемое:

  • Linux
  • con (которое должно быть именем динамически установленного атрибута)

Я получаю:

  • Linux
  • условие (имя значения (несвязанного self.condition) атрибута)

Но я ожидаю, что f.con.__name__ вернет имя атрибута (con), а не имя несвязанной функции (condition), назначенной ему.

Может кто-нибудь объяснить мне, почему это поведение такое и как я могу обойти это?

Спасибо.

Ответы [ 2 ]

1 голос
/ 19 марта 2019

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 - другие реализации могут реализовывать сборку мусора по-разному).

0 голосов
/ 19 марта 2019

Могу я предложить вам следующее:

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...