Jak wywołać setattr () w bieżącym module?

140

Co mam przekazać jako pierwszy parametr " object" do funkcji setattr(object, name, value), aby ustawić zmienne w bieżącym module?

Na przykład:

setattr(object, "SOME_CONSTANT", 42);

dające taki sam efekt jak:

SOME_CONSTANT = 42

w module zawierającym te linie (z poprawnym object).

Generuję kilka wartości na poziomie modułu dynamicznie, a ponieważ nie mogę zdefiniować __getattr__na poziomie modułu, jest to moja rezerwa.

Matt Joiner
źródło

Odpowiedzi:

220
import sys

thismodule = sys.modules[__name__]

setattr(thismodule, name, value)

lub bez użycia setattr(co łamie literę pytania, ale spełnia te same cele praktyczne ;-):

globals()[name] = value

Uwaga : w zakresie modułu ta ostatnia jest równoważna z:

vars()[name] = value

co jest nieco bardziej zwięzłe, ale nie działa z poziomu funkcji ( vars()podaje zmienne z zakresu, w którym jest wywoływany: zmienne modułu, gdy jest wywoływany w zakresie globalnym, a następnie można go użyć R / W, ale funkcja jest zmienne, gdy są wywoływane w funkcji, a następnie muszą być traktowane jako R / O - dokumentacja online Pythona może być nieco myląca co do tego konkretnego rozróżnienia).

Alex Martelli
źródło
9
Dokumentacja ostrzega przed modyfikowaniem funkcji vars (). docs.python.org/library/functions.html#vars . Kiedy można to zrobić?
unutbu
2
@ ~ unutbu, naprawdę nie powiedziałbym, że jest to całkiem „w porządku”, ale zadziała, gdy wywołasz vars()zakres na poziomie modułu, a nie wewnątrz funkcji.
Mike Graham
4
vars()jest równoważne z globals()zakresem modułu (i dlatego zwraca prawdziwe, modyfikowalne polecenie), ale z locals()zakresem funkcji (i dlatego zwraca pseudodict, którego nigdy nie można modyfikować). Używam vars()w zakresie modułu, ponieważ zapisuje 3 znaki, jedną sylabę, w porównaniu z jej synonimem w tym zakresie globals();-)
Alex Martelli
14
Tak, zniszczyłoby to jedną najważniejszą optymalizację, którą wykonuje kompilator Pythona: zmienne lokalne funkcji nie są przechowywane w dyktandzie, są w ciasnym wektorze wartości, a każdy dostęp do zmiennej lokalnej korzysta z indeksu w tym wektorze, a nie wyszukiwanie nazwy. Aby pokonać optymalizację, wymuszając dyktowanie, które chcesz istnieć, uruchom funkcję od exec '': zmień czas na funkcję z kilkoma istotnymi pętlami w każdą stronę, a zobaczysz znaczenie tej podstawowej optymalizacji dla wydajności Pythona.
Alex Martelli
3
@msw, myślę, że zapomniałeś: „praktyczność przewyższa czystość” ;-).
Alex Martelli
6

Jeśli musisz ustawić zmienne o zakresie modułu z poziomu modułu, co jest nie tak global?

# my_module.py

def define_module_scoped_variables():
    global a, b, c
    a, b, c = 'a', ['b'], 3

a zatem:

>>> import my_module
>>> my_module.define_module_scoped_variables()
>>> a
NameError: name 'a' is not defined
>>> my_module.a
'a'
>>> my_module.b
['b']
msw
źródło
1
Tak, zawsze (gdzie „zawsze” definiuje się jako „kilka ostatnich miesięcy, w których uczyłem się Pythona”) uważałem tę global but not reallydeklarację za zagadkową. Przypuszczam, że może to być historyczny relikt, który poprzedza przestrzenie nazw modułów.
msw
1
Pierwotne pytanie dotyczy tego, jak ustawić atrybut, którego nazwa jest podana przez ciąg znaków (to samo, czego szukałem obecnie), aby to nie pomogło.
Curt
6

W Pythonie 3.7 będziesz mógł używać __getattr__na poziomie modułu ( powiązana odpowiedź ).

Za PEP 562 :

def __getattr__(name):
    if name == "SOME_CONSTANT":
        return 42
    raise AttributeError(f"module {__name__} has no attribute {name}")
Trey Hunner
źródło
-1
  1. Nie zrobiłbyś tego. Ty byś zrobiłglobals()["SOME_CONSTANT"] = 42
  2. Nie zrobiłbyś tego. Możesz przechowywać dynamicznie generowaną zawartość w innym miejscu niż moduł.
Mike Graham
źródło
Tak, SOME_CONSTANTobliczone w czasie wykonywania nie jest dokładnie stałe. A jeśli globals()nie jest dla Ciebie dostępny, musisz sięgnąć do innego modułu, aby zmodyfikować jego atrybuty; to z pewnością sprawi, że ludzie zaczną się zastanawiać.
msw
3
Stałe i zmienne wykluczają się wzajemnie. Stałe i dynamicznie generowane nie są. Generowane przeze mnie wartości są zawsze takie same i ustalane na podstawie dalszych „stałych”, aby zaoszczędzić na arytmetyce i wpisywaniu z mojej strony.
Matt Joiner