Atrybuty funkcji Pythona-zastosowania i nadużycia [zamknięte]

Niewielu jest świadomych tej funkcji, ale funkcje (i metody) Pythona mogą mieć atrybuty . Oto:

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

Jakie są możliwe zastosowania i nadużycia tej funkcji w Pythonie ? Jednym z dobrych zastosowań, o których wiem, jest użycie PLY w celu powiązania reguły składni z metodą. Ale co z niestandardowymi atrybutami ? Czy istnieją dobre powody, aby z nich korzystać ?

Author: Eli Bendersky, 2008-12-03

8 answers

Zazwyczaj używam atrybutów funkcji jako magazynu dla adnotacji. Przypuśćmy, że chcę pisać w stylu C# (wskazującym, że pewna metoda powinna być częścią interfejsu web service)

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

Wtedy mogę zdefiniować

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

Następnie, gdy przychodzi wywołanie webservice, poszukuję metody, sprawdzam, czy podstawowa funkcja ma atrybut is_webmethod( rzeczywista wartość jest nieistotna) i odmawiam usługi, jeśli metoda jest nieobecna lub nie ma być wywołana przez www.

 130
Author: Martin v. Löwis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-03 18:06:51

Użyłem ich jako zmiennych statycznych dla funkcji. Na przykład, biorąc pod uwagę następujący kod C:

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

Mogę zaimplementować funkcję w podobny sposób w Pythonie:

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

To z pewnością spadnie na "nadużycia"końca spektrum.

 108
Author: mipadi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-04 22:01:40

Możesz robić obiekty w sposób JavaScript... To nie ma sensu, ale działa;)

>>> def FakeObject():
...   def test():
...     print "foo"
...   FakeObject.test = test
...   return FakeObject
>>> x = FakeObject()
>>> x.test()
foo
 42
Author: defnull,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-04 13:53:06

Używam ich oszczędnie, ale mogą być całkiem wygodne:

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

Teraz mogę używać log w całym module i przekierowywać wyjście po prostu przez ustawienie log.logfile. Istnieje wiele, wiele innych sposobów, aby to osiągnąć, ale ten jest lekki i prosty w brudzie. I chociaż dziwnie pachniało za pierwszym razem, kiedy to zrobiłem, doszedłem do przekonania, że pachnie lepiej niż globalna zmienna logfile.

 12
Author: Robert Rossney,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-07 18:20:06

Atrybuty funkcji mogą być używane do zapisywania lekkich zamknięć, które zawijają kod i Powiązane Dane razem:

#!/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

 10
Author: Kevin Little,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-03 20:27:41

Czasami używam atrybutu funkcji do buforowania już obliczonych wartości. Możesz również mieć ogólny dekorator, który uogólnia to podejście. Bądź świadomy problemów współbieżności i skutków ubocznych takich funkcji!

 3
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-04 08:43:52

Zawsze zakładałem, że jedynym powodem, dla którego jest to możliwe, jest to, że istnieje logiczne miejsce, aby umieścić ciąg doc lub inne tego typu rzeczy. Wiem, że jeśli użyłbym go do jakiegoś kodu produkcyjnego, zdezorientowałoby to większość czytających.

 1
Author: Dale Reidy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-17 14:21:56

Stworzyłem ten pomocniczy dekorator, aby łatwo ustawić atrybuty funkcji:

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

Przypadek użycia polega na utworzeniu kolekcji fabryk i zapytaniu typu danych, które mogą utworzyć na poziomie meta funkcji.
Na przykład (bardzo głupi):

@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
 0
Author: DiogoNeves,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-26 18:50:05