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ć ?
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.
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.
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
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
.
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
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!
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.
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
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