2010-07-08 20 views
7

Mam kilka funkcji (poza dowolną klasą), na których ustawiłem atrybuty, takie jak funcname.fields = 'xxx'. Miałem nadzieję, że mógłbym wówczas dostęp do tych zmiennych od wewnątrz funkcji z self.fields, ale oczywiście to mówi mi:"Ja" wewnątrz funkcji zwykłej?

Global Name „ja” nie jest zdefiniowane

Więc ... co mogę zrobić? Czy jest jakaś magiczna zmienna, do której mogę uzyskać dostęp? Jak __this__.fields?


Kilka osób zapytało "dlaczego?". Prawdopodobnie nie zgodzisz się z moim rozumowaniem, ale mam zestaw funkcji, które wszystkie muszą mieć ten sam podpis (przyjąć tylko jeden argument). W większości przypadków ten jeden argument wystarczy, aby wykonać wymagane obliczenia. Jednak w kilku ograniczonych przypadkach potrzebne są dodatkowe informacje. Zamiast zmuszać każdą funkcję do akceptowania długiej listy najczęściej niewykorzystywanych zmiennych, postanowiłem ustawić je tak, aby można je było zignorować.

Chociaż, wydaje mi się, że teraz można użyć **kwargs jako ostatniego argumentu, jeśli nie dbają o dodatkowe argumenty. No cóż ...

Edytuj: Właściwie niektóre funkcje, których nie napisałem, a raczej nie będą modyfikować, aby zaakceptować dodatkowe argumenty. Poprzez "przekazywanie" dodatkowych argumentów jako atrybutów, mój kod może działać zarówno z moimi niestandardowymi funkcjami, które wykorzystują dodatkowe argumenty, jak iz kodem strony trzeciej, który nie wymaga dodatkowych argumentów.

Dzięki za odpowiedzi :) Speedy

+0

Jak widać, można to zrobić, ale lepszym pytaniem jest * dlaczego ?? * –

+0

@Wayne: Zobacz aktualizację. – mpen

+0

'** kwargs' * prawdopodobnie * prawdopodobnie sprawi, że twoja intencja będzie wyraźniejsza ... i na pewno jest bardziej" pythonic " –

Odpowiedz

2

nie można zrobić, że „funkcja dostępu własne atrybuty” poprawnie we wszystkich sytuacjach - patrz szczegóły tutaj how can python function access its own attributes? - ale tutaj to szybkie demonstracji:

>>> def f(): return f.x 
... 
>>> f.x = 7 
>>> f() 
7 
>>> g = f 
>>> g() 
7 
>>> del f 
>>> g() 
Traceback (most recent call last): 
    File "<interactive input>", line 1, in <module> 
    File "<interactive input>", line 1, in f 
NameError: global name 'f' is not defined 

Zasadniczo większość metod bezpośrednio lub pośrednio polegać na dostęp do obiektu funkcji poprzez wyszukiwanie według nazwy w globaliach; i jeśli oryginalna nazwa funkcji zostanie usunięta, przestanie działać. Są inne sposoby na osiągnięcie tego celu, na przykład definiowanie klasy lub fabryki - ale dzięki waszemu wyjaśnieniu jasne jest, że naprawdę tego nie potrzebujecie.

Wystarczy zrobić wspomnianą kluczowego catch-all argumentu, tak:

def fn1(oneArg): 
    // do the due 

def fn2(oneArg, **kw): 
    if 'option1' in kw: 
     print 'called with option1=', kw['option1'] 
    //do the rest 

fn2(42) 
fn2(42, option1='something') 

Nie wiem, co masz na myśli w swoim komentarzu obchodzenia TypeError - że nie pojawią się podczas korzystania ** kW. To podejście działa bardzo dobrze w przypadku niektórych funkcji systemu Pythona - sprawdź min(), max(), sort(). Ostatnio bardzo mi się przydała sorted(dct,key=dct.get,reverse=True) w CodeGolfie :)

+0

Pojawi się, jeśli pominę '** kw'. Mam kilka starszych funkcji, które akceptują tylko jeden argument. Jeśli przekażę im cały pakiet, wyrzucą "TypeError". Mogę złapać wyjątek, a następnie ponownie wywołać funkcję, ale z jednym argumentem. W każdym razie, jest to dość dokładna odpowiedź, a ty umieszczasz tam miłe ostrzeżenie, o którym inni nie wspomnieli. Nie zdawałem sobie sprawy, że jeszcze nie przyznałem tego pytania ... więc gratulacje! – mpen

+0

Dziękuję. Cóż, być może możesz wstawić argument "brakujący" w definicji starszego fn przy pomocy dekoratora, tak jak w tej nowo wysłanej odpowiedzi http://stackoverflow.com/questions/3109289#3209862 –

2

Przykład:

>>> def x(): pass 
>>> x 
<function x at 0x100451050> 
>>> x.hello = "World" 
>>> x.hello 
"World" 

Można ustawić atrybuty funkcji, jak to tylko zwykłe przedmioty, ale w rzeczywistości nigdy nie widziałem czegoś takiego w realnym kodzie.

Plus. self nie jest słowem kluczowym, tylko inną nazwą zmiennej, która jest szczególnym wystąpieniem klasy. self jest przekazywane niejawnie, ale otrzymane jawnie.

+0

Tak, ale pytałem o to, jak uzyskać dostęp do 'x.hello' z * wewnątrz *' x() '. Ale myślę, że odpowiedź brzmi "w ten sam sposób". Nie podoba mi się jednak, że nazwa funkcji jest związana. – mpen

1

jeśli chcesz globalnie ustawić parametry dla wywoływanej "rzeczy", zawsze możesz utworzyć klasę i zaimplementować metodę __call__?

+0

To właśnie miałem zamiar zrobić ... ale wtedy potrzebujesz dodatkowego zestawu '()' s, aby go najpierw zainicjować. – mpen

11

self nie jest słowem kluczowym w pythonie, jest to zwykła nazwa zmiennej. Podczas tworzenia metod instancji możesz nazwać pierwszy parametr, który chcesz, self to tylko konwencja.

prawie zawsze powinny Wolisz przekazując argumenty do funkcji na właściwości nastawienia wejścia, ale jeśli trzeba, można to zrobić za pomocą rzeczywiste funkcje wymienić dostęp do zmiennych w nim:

def a: 
    if a.foo: 
     #blah 

a.foo = false 
a() 

zobaczyć python function attributes - uses and abuses, gdy przydaje się to. : D

+0

Jest kilka rzeczy, których nie możesz nie zauważyć. –

0

Co dokładnie masz nadzieję, że "ja" wskazywałoby, jeśli funkcja jest zdefiniowana poza jakąkolwiek klasą? Jeśli twoja funkcja potrzebuje pewnych informacji globalnych do prawidłowego wykonania, musisz wysłać tę informację do funkcji w postaci argumentu.

Jeśli chcesz, aby twoja funkcja była świadoma kontekstu, musisz zadeklarować ją w ramach obiektu.

3
def foo(): 
    print(foo.fields) 
foo.fields=[1,2,3] 

foo() 
# [1, 2, 3] 

Nie ma nic złego w dodawaniu atrybutów do funkcji. Wiele memoizerów używa tego do buforowania wyników w samej funkcji.

Na przykład, zwróć uwagę na wykorzystanie func.cache:

from decorator import decorator 
@decorator 
def memoize(func, *args, **kw): 
    # Author: Michele Simoniato 
    # Source: http://pypi.python.org/pypi/decorator 
    if not hasattr(func, 'cache'): 
     func.cache = {} 
    if kw: # frozenset is used to ensure hashability 
     key = args, frozenset(kw.iteritems()) 
    else: 
     key = args 
    cache = func.cache # attribute added by memoize 
    if key in cache: 
     return cache[key] 
    else: 
     cache[key] = result = func(*args, **kw) 
     return result 
1

Nie ma specjalny sposób, w ciele funkcji jest, aby odnieść się do obiektu funkcyjnego, którego kod jest wykonywany. Najprostszy jest po prostu użycie funcname.field (z funcname jest to nazwa funkcji w obszarze nazw, w którym się znajduje, co wskazujesz jest - w przeciwnym razie byłoby trudniej).

1

To nie jest coś, co powinieneś zrobić. Nie mogę wymyślić żadnego sposobu, by zrobić to, o co prosisz, oprócz tego, że chodzisz po stosie wywołań i dziwacznej introspekcji - co nie jest czymś, co powinno się zdarzyć w kodzie produkcyjnym.

Mimo to, myślę, że to rzeczywiście robi to, co pytasz:

import inspect 

_code_to_func = dict() 
def enable_function_self(f): 
    _code_to_func[f.func_code] = f 
    return f 

def get_function_self(): 
    f = inspect.currentframe() 
    code_obj = f.f_back.f_code 
    return _code_to_func[code_obj] 

@enable_function_self 
def foo(): 
    me = get_function_self() 
    print me 

foo() 
1

Chociaż zgadzam się z resztą, że to nie jest chyba dobry projekt, pytanie nie intrygują mnie. Oto moje pierwsze rozwiązanie, które mogę zaktualizować, gdy tylko otrzymam dekoratory. W obecnej formie, opiera się ona dość mocno na możliwość zapoznania się z komina, który może nie być możliwe we wszystkich implementacjach (coś około sys._getframe() niekoniecznie jest obecny ...)

import sys, inspect 

def cute(): 
    this = sys.modules[__name__].__dict__.get(inspect.stack()[0][3]) 
    print "My face is..." + this.face 

cute.face = "very cute" 
cute() 

Co prawda myśleć? : 3

+0

Cóż, jest zdecydowanie słodka ... i ja * używam podobnego "hacka" w moim kodzie, który już korzysta ze stosu. Jednak ... masz rację.Zdecydowanie musi to być dekorator;) I myślę, że określenie nazwy funkcji jest mniejszym złem. – mpen

1

Można użyć następujących (potwornie brzydki) kod:

class Generic_Object(object): 
    pass 

def foo(a1, a2, self=Generic_Object()): 
    self.args=(a1,a2) 
    print "len(self.args):", len(self.args) 
    return None 

... jak widać to pozwalają na skorzystanie z „ja”, jak to opisano. Nie można użyć obiektu "object()" bezpośrednio, ponieważ nie można wprowadzić wartości "monkey patch (*)" w instancji object(). Jednak normalne podklasy obiektu (np. Generic_Object(), które tutaj pokazałem) mogą być "załatane małymi"

Jeśli chcesz zawsze wywoływać swoją funkcję z odniesieniem do jakiegoś obiektu jako pierwszym argumentem, który byłby możliwy. Możesz najpierw wstawić domyślny argument, a następnie * args i opcjonalne ** parametry kwargs (przez które można przekazać inne argumenty lub słowniki opcji podczas wywołań do tej funkcji).

To jest, jak powiedziałem: ohydnie brzydki. Nie publikuj nigdy tego kodu ani nie udostępniaj go nikomu ze społeczności Python. Pokazuję to tutaj tylko jako rodzaj dziwnego ćwiczenia edukacyjnego.

Metoda instancji jest podobna do funkcji w Pythonie. Jednak istnieje on w przestrzeni nazw klasy (dlatego musi być dostępny poprzez instancję ... myobject.foo()) i jest wywoływany z odniesieniem do "self" (analogicznie do "tego" wskaźnika w C++) jako pierwszy argument. Istnieje również proces rozstrzygania metod, który powoduje, że interpreter przeszukuje przestrzeń nazw instancji, następnie klasę, a następnie każdą z klas nadrzędnych i tak dalej ... aż do drzewa dziedziczenia.

Funkcja niezwiązana jest wywoływana z dowolnymi argumentami, które do niej przekazywane. Nie może być żadnego rodzaju automatycznie zindeksowanego obiektu/odniesienia do instancji do listy argumentów. Zatem pisanie funkcji z początkowym argumentem o nazwie "self" jest bez znaczenia. (Jest to legalne, ponieważ Python nie umieszcza żadnego szczególnego znaczenia na nazwie "self". Ale bez znaczenia, ponieważ osoby dzwoniące do twojej funkcji będą musiały ręcznie dostarczyć jakiś obiektowy odnośnik do listy argumentów i nie jest wcale jasne, co to powinno być Po prostu jakiś dziwaczny "Generic_Object", który następnie unosi się w przestrzeni zmiennej globalnej?).

Mam nadzieję, że trochę to wyjaśnię. Wygląda na to, że cierpisz z powodu bardzo podstawowych nieporozumień na temat działania Pythona i innych systemów obiektowych.

  • („Monkey łatania” jest terminem stosowanym do opisania bezpośredniego manipulacje dokonywane obiektów atrybutami - albo „zmienne Wystąpienie” kodem, który nie jest częścią hierarchii klas, których przedmiotem jest przykładem).
+0

N ... Myślę, że wiedziałem, jak pracowałem, po prostu nie przemyślałem tego, kiedy zaczynałem tę ścieżkę :) Większość innych obiektów, z którymi pracuję * to * klasy, więc działało aż do teraz. Wtedy zdecydowałem się zdobyć całą kreatywność i powiedziałem: "cóż ... co jeśli nie chcę stworzyć całej klasy tylko po to ?!" i ... tak. W każdym razie, opracowałem jakąś hybrydową metodę ze wskazówkami twoich facetów! Więc dziękuję. Zauważyłem niedawno, że zwykłe obiekty nie pozwalają na łatanie małp ... Zastanawiam się, dlaczego tak jest? Czyniłoby ich o wiele bardziej użytecznym IMO. – mpen

1

Jako inną alternatywę, można dokonać w funkcje związane metod klasy tak:

class _FooImpl(object): 
    a = "Hello " 
    @classmethod 
    def foo(cls, param): 
     return cls.a + param 
foo = _FooImpl.foo 

# later... 
print foo("World") # yes, Hello World 

# and if you have to change an attribute: 
foo.im_self.a = "Goodbye " 

Jeśli chcesz podzielić namespaecs funkcje atrybutów, po prostu uczynić je częścią tej samej klasie. Jeśli nie, daj każdemu swoją własną klasę.