Deklaracja zmiennej Pythona

86

Nauka Pythona i ma kilka podstawowych wątpliwości.

1.Widziałem deklarację zmiennej (tutaj ścieżka) jako

class writer:
    path = ""

czasami bez jawnej deklaracji, ale inicjalizuj za pośrednictwem __init__.

def __init__(self, name):
    self.name = name

Rozumiem cel __init__, ale czy wskazane jest deklarowanie zmiennej w innych funkcjach.

2.Jak mogę stworzyć zmienną do przechowywania niestandardowego typu?

class writer:
    path = "" # string value
    customObj = ??
bsr
źródło
12
A jeśli chodzi o drugie pytanie: w Pythonie zmienne nie mają typu: wartości. Tak więc każda zmienna może przechowywać Twoje obiekty niestandardowe.
jpaugh
4
Pomoże to, jeśli przestaniesz myśleć o nazwach / identyfikatorach jako o zmiennych . Są to odniesienia do obiektów
John La Rooy
2
@gnibbler Albo imiona dla nich ...
detly
1
@detly, myślę, że nazwy to zły wybór. Rzeczy zwykle mają tylko jedną nazwę, podczas gdy obiekt może mieć wiele odniesień
John La Rooy,
2
@JohnClements, ale Python nie działa jak matematyka. Jeśli chcesz zrozumieć Pythona, musisz wiedzieć, że obiekty z __name__atrybutem mają „nazwę”. Nie wszystkie obiekty mają __name__atrybut, ale nadal możesz mieć do nich odniesienia. Jeśli zaczniemy nazywać „odniesienie” „imieniem”, początkującym wyrządzimy krzywdę na tym torze.
John La Rooy

Odpowiedzi:

202

Dobra, najpierw najważniejsze.

W Pythonie nie ma czegoś takiego jak „deklaracja zmiennej” czy „inicjalizacja zmiennej”.

Istnieje po prostu coś, co nazywamy „przypisaniem”, ale prawdopodobnie powinno to być po prostu nazwane „nazewnictwem”.

Przypisanie oznacza „ta nazwa po lewej stronie odnosi się teraz do wyniku oceny prawej strony, niezależnie od tego, do czego się odnosiło wcześniej (jeśli cokolwiek)”.

foo = 'bar' # the name 'foo' is now a name for the string 'bar'
foo = 2 * 3 # the name 'foo' stops being a name for the string 'bar',
# and starts being a name for the integer 6, resulting from the multiplication

Jako takie, nazwy Pythona (prawdopodobnie lepsze określenie niż „zmienne”) nie mają skojarzonych typów; wartości tak. Możesz ponownie zastosować tę samą nazwę do wszystkiego, niezależnie od jego typu, ale rzecz nadal zachowuje się zależnie od jej typu. Nazwa jest po prostu sposobem na odwołanie się do wartości (obiektu). To odpowiada na twoje drugie pytanie: nie tworzysz zmiennych do przechowywania niestandardowego typu. Nie tworzysz zmiennych do przechowywania określonego typu. W ogóle nie „tworzysz” zmiennych. Nadajesz nazwy przedmiotom.

Druga uwaga: Python kieruje się bardzo prostą zasadą, jeśli chodzi o klasy, która jest w rzeczywistości znacznie bardziej spójna niż to, co robią języki takie jak Java, C ++ i C #: wszystko zadeklarowane wewnątrz classbloku jest częścią klasy . Tak więc defnapisane tutaj funkcje ( ) są metodami, tj. Częścią obiektu klasy (nie są przechowywane dla poszczególnych instancji), podobnie jak w Javie, C ++ i C #; ale inne nazwiska tutaj również są częścią klasy. Nazwy są po prostu nazwami i nie mają powiązanych typów, a funkcje są również obiektami w Pythonie. A zatem:

class Example:
    data = 42
    def method(self): pass

Klasy też są obiektami w Pythonie.

Więc teraz stworzyliśmy obiekt o nazwie Example, który reprezentuje klasę wszystkich rzeczy, które są Examples. Ten obiekt ma dwa atrybuty dostarczane przez użytkownika (w C ++ „składowe”; w C # „pola, właściwości lub metody”; w języku Java „pola lub metody”). Jeden z nich ma nazwę datai przechowuje wartość całkowitą 42. Drugi nazywa się methodi przechowuje obiekt funkcji. (Istnieje kilka innych atrybutów, które Python dodaje automatycznie).

Jednak te atrybuty nadal nie są częścią obiektu. Zasadniczo obiekt jest tylko zbiorem większej liczby nazw (nazw atrybutów), dopóki nie przejdziesz do rzeczy, których nie można już podzielić. W ten sposób wartości mogą być współużytkowane między różnymi instancjami klasy, a nawet między obiektami różnych klas, jeśli celowo to ustawisz.

Utwórzmy instancję:

x = Example()

Teraz mamy oddzielny obiekt o nazwie x, który jest instancją Example. dataI methodfaktycznie nie jest częścią obiektu, ale nadal możemy szukać ich poprzez xpowodu jakiejś magii, że Python robi za kulisami. W methodszczególności, gdy spojrzymy w górę , zamiast tego otrzymamy „metodę powiązaną” (kiedy ją wywołasz, xzostanie przekazana automatycznie jako selfparametr, co nie może się zdarzyć, jeśli spojrzymy Example.methodbezpośrednio w górę ).

Co się dzieje, gdy próbujemy użyć x.data?

Kiedy go badamy, najpierw znajduje się w obiekcie. Jeśli nie zostanie znaleziony w obiekcie, Python szuka w klasie.

Jednak kiedy przypiszemy do x.data , Python utworzy atrybut na obiekcie. To będzie nie zastąpi atrybut class'.

To pozwala nam na inicjalizację obiektów . Python automatycznie wywoła __init__metodę klasy w nowych instancjach, gdy zostaną utworzone, jeśli są obecne. W tej metodzie możemy po prostu przypisać do atrybutów, aby ustawić wartości początkowe dla tego atrybutu na każdym obiekcie:

class Example:
    name = "Ignored"
    def __init__(self, name):
        self.name = name
    # rest as before

Teraz musimy określić, namekiedy tworzymy Example, a każda instancja ma swoją własną name. Python zignoruje atrybut class Example.nameza każdym razem, gdy wyszukamy .nameinstancję, ponieważ atrybut instancji zostanie znaleziony jako pierwszy.

Ostatnie zastrzeżenie: modyfikacja (mutacja) i przypisanie to dwie różne rzeczy!

W Pythonie ciągi znaków są niezmienne. Nie można ich modyfikować. Kiedy robisz:

a = 'hi '
b = a
a += 'mom'

Nie zmieniasz oryginalnego ciągu „cześć”. To niemożliwe w Pythonie. Zamiast tego tworzysz nowy łańcuch 'hi mom'i powodujesz, aże przestajesz być nazwą dla 'hi ', a 'hi mom'zamiast tego zaczynasz być nazwą . Stworzyliśmy brównież nazwę dla 'hi ', a po ponownym zastosowaniu anazwy bnadal jest nazwą dla 'hi ', ponieważ 'hi 'nadal istnieje i nie została zmieniona.

Ale listy można zmienić:

a = [1, 2, 3]
b = a
a += [4]

Teraz bjest również [1, 2, 3, 4], ponieważ nadaliśmy bnazwę tej samej rzeczy, która została anazwana, a potem ją zmieniliśmy. Nie utworzyliśmy nowej listy dla ado nazwania, ponieważ Python po prostu traktuje +=listy inaczej.

Ma to znaczenie dla obiektów, ponieważ gdybyś miał listę jako atrybut klasy i używał instancji do modyfikowania listy, wtedy zmiana byłaby „widoczna” we wszystkich innych instancjach. Dzieje się tak, ponieważ (a) dane są w rzeczywistości częścią obiektu klasy, a nie obiektu instancji; (b) ponieważ modyfikowałeś listę i nie wykonywałeś prostego przypisania, nie utworzyłeś nowego atrybutu instancji, który ukrywałby atrybut klasy.

Karl Knechtel
źródło
12
Dlatego nazywamy je „identyfikatorami” . :-)
Martijn Pieters
@Karl_Knechtel na końcu twojego przykładu ze stringiem, co stanie się z „cześć”, jeśli nigdy nie nazwiemy go „b”? Czy to wyciek pamięci?
nieskończoności
2
@heretoinfinity: no; Python używa czyszczenia pamięci, aby uwolnić nieosiągalne obiekty
John Clements,
Deklaracja zmiennej jest dobrze wyjaśniona w tym filmie: youtube.com/watch?v=ao2N37E-D9s .
Ishaq Khan
obejrzyj ten film, aby uzyskać odpowiedź youtube.com/…
Thao N
23

To może być opóźnione o 6 lat, ale w Pythonie 3.5 i nowszych deklarujesz typ zmiennej, taki jak ten:

variable_name: type_name

albo to:

variable_name # type: shinyType

Więc w twoim przypadku (jeśli masz CustomObjectzdefiniowaną klasę), możesz zrobić:

customObj: CustomObject

Zobacz to czy tamto, aby uzyskać więcej informacji.

O. Aroesti
źródło
22

Nie ma potrzeby deklarowania nowych zmiennych w Pythonie. Jeśli mówimy o zmiennych w funkcjach lub modułach, nie jest potrzebna żadna deklaracja. Wystarczy przypisać wartość do nazwy, gdzie jest to potrzebne: mymagic = "Magic". Zmienne w Pythonie mogą przechowywać wartości dowolnego typu i nie można tego ograniczyć.

Twoje pytanie dotyczy jednak klas, obiektów i zmiennych instancji. Idiomatyczny sposób tworzenia zmiennych instancji znajduje się w __init__metodzie i nigdzie indziej - podczas gdy można tworzyć nowe zmienne instancji w innych metodach lub nawet w niepowiązanym kodzie, to po prostu zły pomysł. Sprawi to, że Twój kod będzie trudny do zrozumienia lub utrzymania.

Na przykład:

class Thing(object):

    def __init__(self, magic):
        self.magic = magic

Łatwy. Teraz instancje tej klasy mają magicatrybut:

thingo = Thing("More magic")
# thingo.magic is now "More magic"

Tworzenie zmiennych w przestrzeni nazw samej klasy prowadzi do zupełnie innego zachowania. Funkcjonalnie jest inny i powinieneś to robić tylko wtedy, gdy masz do tego konkretny powód. Na przykład:

class Thing(object):

    magic = "Magic"

    def __init__(self):
        pass

Spróbuj teraz:

thingo = Thing()
Thing.magic = 1
# thingo.magic is now 1

Lub:

class Thing(object):

    magic = ["More", "magic"]

    def __init__(self):
        pass

thing1 = Thing()
thing2 = Thing()
thing1.magic.append("here")
# thing1.magic AND thing2.magic is now ["More", "magic", "here"]

Dzieje się tak, ponieważ przestrzeń nazw samej klasy różni się od przestrzeni nazw utworzonych z niej obiektów. Zostawię ci trochę więcej badań.

Wiadomość do domu jest taka, że ​​idiomatyczny Python ma (a) inicjować atrybuty obiektów w Twojej __init__metodzie i (b) dokumentować zachowanie Twojej klasy zgodnie z potrzebami. Nie musisz kłopotać się pełną dokumentacją na poziomie Sfinksa dla wszystkiego, co kiedykolwiek napiszesz, ale przynajmniej kilka komentarzy na temat wszelkich szczegółów, których ty lub ktoś inny może potrzebować, aby ją odebrać.

bezczelnie
źródło
12

W celu określenia zakresu używam:

custom_object = None
czerwony gorący
źródło
1

Zmienne mają zakres, więc tak, warto mieć zmienne, które są specyficzne dla twojej funkcji. Nie zawsze musisz jasno określać ich definicję; zwykle możesz ich po prostu użyć. Tylko jeśli chcesz zrobić coś specyficznego dla typu zmiennej, np. Dołączyć do listy, musisz je zdefiniować przed rozpoczęciem ich używania. Typowy tego przykład.

list = []
for i in stuff:
  list.append(i)

Nawiasem mówiąc, to nie jest dobry sposób na ustawienie listy. Lepiej byłoby powiedzieć:

list = [i for i in stuff] # list comprehension

... ale błądzę.

Twoje inne pytanie. Obiekt niestandardowy powinien być klasą.

class CustomObject(): # always capitalize the class name...this is not syntax, just style.
  pass
customObj = CustomObject()
ChipJust
źródło