Функциям Python могут быть даны новые атрибуты за пределами области видимости? - PullRequest
2 голосов
/ 18 ноября 2009

Я не знал, что вы могли бы сделать это:

def tom():
    print "tom's locals: ", locals()

def dick(z):
    print "z.__name__ = ", z.__name__
    z.guest = "Harry"
    print "z.guest = ", z.guest
    print "dick's locals: ", locals()

tom()              #>>> tom's locals:  {}
#print tom.guest    #AttributeError: 'function' object has no attribute 'guest'
print "tom's dir:", dir(tom)  # no 'guest' entry

dick( tom)         #>>> z.__name__ =  tom
                   #>>> z.guest =  Harry
                   #>>> dick's locals:  {'z': <function tom at 0x02819F30>}
tom()              #>>> tom's locals:  {}
#print dick.guest  #AttributeError: 'function' object has no attribute 'guest'

print tom.guest    #>>> Harry
print "tom's dir:", dir(tom)  # 'guest' entry appears

Функция tom () не имеет местных жителей. Функция dick () знает, где живет tom (), и выставляет Гарри в качестве гостя на месте tom (). Гарри не появляется как местный житель на месте Тома (), но если вы попросите гостя Тома, Гарри ответит. Гарри - это новый атрибут в Tom ().

ОБНОВЛЕНИЕ: Снаружи tom () вы можете сказать «print dir (tom)» и посмотреть словарь объекта tom. (Вы также можете сделать это из внутри tom (). Чтобы Том мог узнать, что у него новый постоялец, Гарри, под именем «гость».)

Итак, атрибуты могут быть добавлены в пространство имен функции вне функции? Это часто делается? Это приемлемая практика? Рекомендуется ли в некоторых ситуациях? Это на самом деле жизненно важно время от времени? (Это Pythonic?)

ОБНОВЛЕНИЕ: заголовок теперь говорит «атрибуты»; раньше говорили «переменные». Вот PEP об атрибутах функций .

Ответы [ 7 ]

4 голосов
/ 18 ноября 2009

Я думаю, что вы можете смешивать понятия локальные переменные и атрибуты функции . Для получения дополнительной информации об атрибутах функции Python см. Вопрос SO Атрибуты функции Python - использует и злоупотребляет .

4 голосов
/ 18 ноября 2009

@ позади, мотивация придать объектам функций общие присваиваемые атрибуты (они не использовали их), заключалась в том, что, при отсутствии таких возможностей, реальные и популярные структуры злоупотребляли тем немногим присваиваемым атрибутам , существовавших ( обычно __doc__) для записи информации о каждом заданном функциональном объекте. Таким образом, явно существовал «отложенный спрос» на эту функциональность, поэтому Гвидо решил обратиться к ней напрямую (добавление необязательного dict к каждому объекту функции для записи его атрибутов не имеет большого значения - большинство объектам функций это не нужно, и является необязательным , поэтому стоимость нулевого указателя составляет всего 4 байта; -).

Назначение таких атрибутов в произвольных местах было бы очень плохой практикой, затрудняя понимание кода без реальной выгоды, но они очень полезны при использовании контролируемым образом - например, декоратор может с пользой записывать все виды вещей о декорируемой функции и контексте, в котором происходило декорирование, в качестве атрибутов функции-оболочки, позволяющей легко и просто анализировать такие метаданные позже в любое время, когда это необходимо.

Как уже указывалось в других ответах, локальные переменные (которые относятся к экземпляру, а не к объекту функции!) Являются полностью непересекающимся пространством имен из атрибутов объекта функции, хранящихся в его __dict__.

3 голосов
/ 18 ноября 2009

В python пространство имен - это просто объект словаря, отображающий имя переменной в виде строки (в данном случае, «guest»), в значение (в данном случае, «Harry»). Поэтому, пока у вас есть доступ к объекту, и он является изменяемым, вы можете изменить все, что касается его пространства имен.

В небольших проектах это не такая уж большая проблема, и она позволяет быстрее объединять вещи, но невероятно запутывает в больших проектах, где ваши данные могут быть изменены из любого места.

Существуют способы сделать атрибуты классов "более приватными", такие как Имя искажения .

1 голос
/ 18 ноября 2009

Я использовал это в прошлом, чтобы создать автономную функцию с «перечислениями», которые сопровождают ее.

Предположим, я реализовал функцию seek(). Встроенный Python one (для файловых объектов) принимает целое число, чтобы сообщить ему, как работать; фу, дай мне перечисление, пожалуйста.

def seek(f, offset, whence=0):
    return f.seek(offset, whence)

seek.START = 0
seek.RELATIVE = 1
seek.END = 2

f = open(filename)

seek(f, 0, seek.START)  # seek to start of file
seek(f, 0, seek.END)  # seek to end of file

Как вы думаете, слишком сложно и странно? Мне нравится, как он хранит значения enum вместе с функцией; если вы импортируете функцию из модуля, вы также автоматически получаете значения enum.

1 голос
/ 18 ноября 2009

tom.guest - это просто свойство объекта-функции tom, оно не имеет ничего общего с областью действия или locals () внутри этой функции, и не имеет ничего общего с тем фактом, что tom является функцией, оно будет работать с любым объект.

0 голосов
/ 18 ноября 2009

Инструменты генерации документации Python API, такие как pydoc и epydoc , используют интроспекцию для определения имени функции и строки документации (доступны как атрибуты __name__ и __doc__). Ожидается, что декораторы функций с хорошим поведением сохранят эти атрибуты, поэтому такие инструменты продолжают работать должным образом (то есть декорирование функции должно сохранять документацию декорированной функции). Вы делаете это путем копирования этих атрибутов из оформленной функции в декоратор. Взгляните на update_wrapper в модуле functools:

WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__')
WRAPPER_UPDATES = ('__dict__',)

def update_wrapper(wrapper,
                   wrapped,
                   assigned = WRAPPER_ASSIGNMENTS,
                   updated = WRAPPER_UPDATES):
    """Update a wrapper function to look like the wrapped function

       wrapper is the function to be updated
       wrapped is the original function
       ...
    """
    for attr in assigned:
        setattr(wrapper, attr, getattr(wrapped, attr))
    for attr in updated:
        getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
    ...

Итак, это как минимум один пример, когда изменение атрибутов функции полезно и приемлемо.

В некоторых ситуациях полезно «аннотировать» функцию путем установки атрибута; Django использует это в нескольких местах:

  • Вы можете установить alters_data в True на модельных методах, которые меняют базы данных, предотвращая их вызывается в шаблонах.

  • Вы можете установить allow_tags на модельных методах, которые будет отображаться в админке, чтобы означает, что метод возвращает HTML контент, который не должен быть автоматически сбежал.

Как всегда, используйте свое суждение. Если изменение атрибутов является общепринятой практикой (например, при написании декоратора), тогда все средства продолжаются. Если он будет частью скважины документированного API, то, вероятно, тоже хорошо.

0 голосов
/ 18 ноября 2009

Функции Python ограничены лексической областью, поэтому невозможно добавить переменные в функцию за пределами ее определенной области.

Однако функция все равно будет иметь доступ ко всем родительским областям, если вы действительно хотели спроектировать систему подобным образом (хотя обычно это считается плохой практикой):

>>> def foo():
>>>     def bar():
>>>         print x
>>>     x = 1
>>>     bar()
1

Мутирование переменных функций в большинстве случаев является плохой идеей, поскольку предполагается, что функции являются неизменяемыми. Наиболее питонический способ реализации этого поведения - вместо этого использовать классы и методы.

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