2008-12-03 3 views
139

Немногие знают об этой функции, но функции (и методы) Python могут иметь attributes.Атрибуты функции Python - использование и злоупотребления

>>> def foo(x): 
...  pass 
...  
>>> foo.score = 10 
>>> dir(foo) 
['__call__', '__class__', '__delattr__', '__dict__', '__doc__', '__get__', '__getattribute__', '__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name', 'score'] 
>>> foo.score 
10 
>>> foo.score += 1 
>>> foo.score 
11 

Каковы возможные применения и злоупотребления этой функцией в Python? Одно хорошее использование, о котором я знаю, это использование PLY использования docstring для связывания правила синтаксиса с помощью метода. Но как насчет пользовательских атрибутов? Есть ли веские причины для их использования?

+1

Это очень удивительно? В общем, объекты Python поддерживают ad-hoc-атрибуты. Конечно, некоторые не делают, особенно со встроенным типом. Для меня те, кто не поддерживает это, похоже, являются исключениями, а не правилом. – allyourcode 2012-11-27 07:01:17

+3

Одно приложение в Django: [Настроить список изменений admin] (https://docs.djangoproject.com/en/1.6/intro/tutorial02/#customize-the-admin-change-list) – 2013-11-15 13:27:22

+1

Проверьте [PEP 232] (http://www.python.org/dev/peps/pep-0232/). – user140352 2009-07-17 18:36:46

ответ

118

Обычно я использую атрибуты функций в качестве хранилища для аннотаций. Предположим, что я хочу написать, в стиле C# (что указывает на определенный метод должен быть частью интерфейса веб-службы)

class Foo(WebService): 
    @webmethod 
    def bar(self, arg1, arg2): 
     ... 

то я могу определить

def webmethod(func): 
    func.is_webmethod = True 
    return func 

Затем, когда вебсервис вызов Прибывает, я просматриваю метод, проверяю, имеет ли базовая функция атрибут is_webmethod (фактическое значение не имеет значения) и отказывается от службы, если метод отсутствует или не предназначен для вызова через Интернет.

94

Я использовал их как статические переменные для функции. Например, учитывая следующий код C:

int fn(int i) 
{ 
    static f = 1; 
    f += i; 
    return f; 
} 

я могу реализовать функцию так же в Python:

def fn(i): 
    fn.f += i 
    return fn.f 
fn.f = 1 

Это, несомненно, попадет в «злоупотребляет» конце спектра.

8

Функциональные атрибуты могут быть использованы для записи замыкания облегченные, которые обертывают код и соответствующие данные вместе:

#!/usr/bin/env python 

SW_DELTA = 0 
SW_MARK = 1 
SW_BASE = 2 

def stopwatch(): 
    import time 

    def _sw(action = SW_DELTA): 

     if action == SW_DELTA: 
     return time.time() - _sw._time 

     elif action == SW_MARK: 
     _sw._time = time.time() 
     return _sw._time 

     elif action == SW_BASE: 
     return _sw._time 

     else: 
     raise NotImplementedError 

    _sw._time = time.time() # time of creation 

    return _sw 

# test code 
sw=stopwatch() 
sw2=stopwatch() 
import os 
os.system("sleep 1") 
print sw() # defaults to "SW_DELTA" 
sw(SW_MARK) 
os.system("sleep 2") 
print sw() 
print sw2() 

1,00934004784

2,00644397736

3,01593494415

3

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

36

Вы можете сделать объекты, как JavaScript ... Это не имеет никакого смысла, но она работает;)

>>> def FakeObject(): 
... def test(): 
...  print "foo" 
... FakeObject.test = test 
... return FakeObject 
>>> x = FakeObject() 
>>> x.test() 
foo 
1

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

8

Я использую их экономно, но они могут быть довольно удобно:

def log(msg): 
    log.logfile.write(msg) 

Теперь я могу использовать log в течение моего модуля, и перенаправить вывод просто установив log.logfile. Есть много и много других способов сделать это, но этот легкий и простой. И хотя это было странно, когда я впервые это сделал, я пришел к выводу, что он пахнет лучше, чем глобальная переменная logfile.

0

Я создал этот помощник декоратора легко установить функции атрибуты:

def with_attrs(**func_attrs): 
    """Set attributes in the decorated function, at definition time. 
    Only accepts keyword arguments. 
    E.g.: 
     @with_attrs(counter=0, something='boing') 
     def count_it(): 
      count_it.counter += 1 
     print count_it.counter 
     print count_it.something 
     # Out: 
     # >>> 0 
     # >>> 'boing' 
    """ 
    def attr_decorator(fn): 
     @wraps(fn) 
     def wrapper(*args, **kwargs): 
      return fn(*args, **kwargs) 

     for attr, value in func_attrs.iteritems(): 
      setattr(wrapper, attr, value) 

     return wrapper 

    return attr_decorator 

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

@with_attrs(datatype=list) 
def factory1(): 
    return [1, 2, 3] 

@with_attrs(datatype=SomeClass) 
def factory2(): 
    return SomeClass() 

factories = [factory1, factory2] 

def create(datatype): 
    for f in factories: 
     if f.datatype == datatype: 
      return f() 
    return None 

 Смежные вопросы

  • Нет связанных вопросов^_^