Próbuję przepisać jakiś kod za pomocą klas. W pewnym momencie, czego chcę przypisać funkcję członka konkretną definicję przy użyciu wartości parametru dla każdej instancji obiektu.

Pochodząc z innych języków (JavaScript, C ++, Haskell, Fortran, ...) Walczą do rozumiem kilka rzeczy na pythonie. Jedna rzecz jest następującym rozróżnieniem metod klasowych self>.

Na przykład następujący kod oczywiście nie działa:

class fdf:
    def f(x):
        return 666

class gdg(fdf):
    def sq():
        return 7*7

hg = gdg()
hf = fdf()
print(hf.f(),hg.f(),hg.sq())

Co daje błąd, który " sq () trwa 0 argumentów pozycyjnych, ale 1 otrzymano ".

Powód, jak to rozumiem, jest to, że w czas wykonania funkcja jest przekazywana odniesienie do obiektu wywołującego (instancja nazywająca SQ) jako pierwszy argument przed jakimkolwiek innym parametrem / argumentem mogliśmy zdefiniować / Nazywany SQ z. Więc rozwiązanie jest proste: Zmień kod SQ do def sq(self):. Rzeczywiście, Python Tutorial To 2 lub To 3, wydaje mi się, że ten kod nie powinien działać ... lub powinien mieć pewne nieoczekiwane konsekwencje Jakiś punkt (?) .

The Nici "Dlaczego nie jest używany w tej metodzie" 4 nie zapewnia dla mnie wyraźnej odpowiedzi : Jakiej szczegółów składni powyższych kodu mówi mi, że self nie jest potrzebna? Na przykład, jak ten kod różni się od tego przykładu samouczka

class MyClass:
    """A simple example class"""
    i = 12345

    def f(self):
        return 'hello world'

? Instancjonowanie tej klasy działa zgodnie z oczekiwaniami, ale skarży się z brakującym parametrem (wiem, że może to być dowolna etykieta), jeśli zdefiniowano bez żadnych.

To sprawia, że kwestionuję, czy mój kod nie ukrywa się jakoś bomba czasu: jest self przekazana jako wartość dla x? Działa zgodnie z oczekiwaniami, więc powiedziałbym nie, ale potem stoję w obliczu tej konundrum.

Myślę, że brakuje mi kilku kluczowych pomysłów języka. Przyznaję, że mam też walkę z pytaniem OP of reference 3 pytam ^.

[^]: W JS po prostu używa this w korpusie funkcji, a sama funkcja jest zdefiniowana jako członek prototypu obiektu lub jako członu instancji, który następnie jest przypisany prawidłowo przy użyciu ... {{X1} }.

Edytuj: Nici jest długi. W przypadku przeglądania pomocy, jeśli jesteś nowy w Pythonie, możesz sprawdzić wybrane rozwiązanie i jego komentarze. Jednakże, jeśli już wiesz o metodach związanych / niezwiązanych w Pythonie, po prostu chcesz sprawdzić korzystanie z deskryptora, jak opisano w odpowiedzi Bleckknghta. W końcu zdecydowałem się w ten sposób w moim kodzie za pomocą __get__ w przypisaniu do uruchomienia.

9
MASL 25 czerwiec 2017, 00:16

4 odpowiedzi

Najlepsza odpowiedź

Co to jest self?

W Pythonie, co metoda normalna jest zmuszona do przyjęcia parametru powszechnie nazywanego self. Jest to instancja klasy - obiekt. W ten sposób metody Pythona współdziałają z stanami klas.

Możesz zmienić nazwę tego parametru, co chcesz. Ale zawsze będzie miała taką samą wartość:

>>> class Class:
    def method(foo): # 
        print(foo)


>>> cls = Class()
>>> cls.method()
<__main__.F object at 0x03E41D90>
>>> 

Ale dlaczego mój przykład działa?

Jednak to, co prawdopodobnie jesteś zdezorientowany, jest jak ten kod działa inaczej:

>>> class Class:
    def method(foo):
        print(foo)

    methods = {'method': method}

    def __init__(self):
        self.run = self.methods['method']


>>> cls = Class()
>>> cls.run(3)
3
>>> 

Jest to ze względu na rozróżnienie między związane , a niezwiązane metody w Pythonie.

Kiedy to robimy w __init__():

self.run = self.methods['method']

Odnosimy się do Metodę method. Oznacza to, że nasze odniesienie do method nie jest związane z instancją specyficzną Class, a zatem Python nie zmusi method, aby zaakceptować instancję obiektów. ponieważ nie ma tego.

Powyższy kod byłby taki sam jak to:

>>> class Class:
    def method(foo):
        print(foo)


>>> Class.method(3)
3
>>> 

W obu przykładach wywołujemy metodę method obiektu klasy Class, i instancji nie obiektu Class .

Możemy dodatkowo zobaczyć to rozróżnienie, badanie repr dla metody związanej i niezwiązanej:

>>> class Class:
    def method(foo):
        print(foo)


>>> Class.method
<function Class.method at 0x03E43D68>
>>> cls = Class()
>>> cls.method
<bound method Class.method of <__main__.Class object at 0x03BD2FB0>>
>>> 

Jak widać, w pierwszym przykładzie, gdy robimy Class.method, Python pokazuje: <function Class.method at 0x03E43D68>. Nieco cię okłamałem. Kiedy mamy niezwiązaną metodę klasy, Python traktuje je jako zwykłe funkcje. Więc method jest po prostu funkcją, która nie jest związana z żadną instancją `klasy.

Jednak w drugim przykładzie, gdy utworzymy instancję Class, a następnie uzyskaj dostęp do obiektu method, widzimy drukowane: <bound method Class.method of <__main__.Class object at 0x03BD2FB0>>.

Część klucza do powiadomienia jest bound method Class.method. Oznacza to, że method oznacza związany do cls - Spektakl instancja Class.

Uwagi ogólne

Jak wspomniał @jonshapre, kod pisania jak w twoim przykładzie prowadzi do zamieszania (jako dowód tego pytania) i błędy. Byłoby lepszym pomysłem, jeśli po prostu zdefiniowałeś nonLinearBipolarStep() outside Activation i odniesienie, które od wewnątrz Activation.activation_functions:

def nonLinearBipolarStep(self,x,string=None):
        if not string: return (-1 if x<0 else 1 )
        else: return ('-' if x<0 else '1')

class Activation:

    activation_functions = {
        'bipolar': nonLinearBipolarStep,
    }

    ...

Myślę, że bardziej konkretne pytanie będzie: co powinienem zwrócić uwagę na ten kod, aby stać się oczywiste, że ag.run(x) byłby wezwanie do niezwiązanej funkcji?

Jeśli nadal chciałbyś pozwolić nonLinearBipolarStep być niezwiązany, a następnie polecam po prostu uważnie. Jeśli uważasz, że twoja metoda uczyniłaby dla najczystszego kodu, a następnie przejdź do niego, ale upewnij się, że wiesz, co robisz, a zachowanie ma Twój kod.

Jeśli nadal chciałeś zrobić, jest jasne dla użytkowników swojej klasy, że ag.run() byłyby statyczne, ty może dokumentuj je w DocString gdzieś, ale jest to coś, co użytkownik naprawdę nie powinien trzeba się martwić.

6
Christian Dean 25 czerwiec 2017, 22:22

Myślę, co cię pomylił tutaj, aby uzyskać dostęp do metody za pomocą atrybutu klasy activationFunctions, a nie (jak zwykle można uzyskać dostęp do samej instancji. Na przykład biorąc pod uwagę:

class Class:

    def method(self, foo, bar):
        print(self, foo, bar)

    methods = {'method': method}

Kiedy nazywamy metodę bezpośrednio ze słownika:

>>> Class.methods['method'](1, 2, 3)
1 2 3

Widać, że przechodzimy 1 jako parametr self; Metoda nie jest wywoływana na instancji, więc żadna instancja nie jest wstrzyknięta. Natomiast, kiedy nazywamy go na instancji:

>>> instance = Class()
>>> instance.method(1, 2)
<__main__.Class object at 0x...> 1 2

Teraz nasze argumenty są foo i bar, a instancja jest self. Dlatego uważasz, że wydaje się, że wymagana jest inna liczba parametrów.


W takim przypadku, ponieważ w rzeczywistości nie potrzebujesz stanu instancji w swojej metodzie, wystarczy sprawić, że jest to regularna funkcja (uwaga niewielkie zmiany dla Pep-8 Zgodność):

def non_linear_bipolar_step(x, string=None):
    if string is not None: 
        return -1 if x < 0 else 1
    return '-' if x < 0 else '1'

class Activation:

    activation_functions = {
        'bipolar': non_linear_bipolar_step,
    }

    ...

Jest to prawdopodobnie mniej mylące.

2
jonrsharpe 24 czerwiec 2017, 22:53

Biegasz w jednej z bardziej subtelnych części wdrażania metody Pythona. Pasa do tego, jak argument self dla normalnych połączeń metod (np some_instance.method()) jest związany. Wykorzystuje protokół "deskryptora", który nie jest zbyt dobrze udokumentowany (przynajmniej nie jest oczywisty dla nowych programistów Python).

Deskryptor jest obiektem, który ma metodę __get__ (i opcjonalnie __set__ i / lub {x2}} metodę, ale tu tylko porozmawiać o __get__ tutaj). Gdy taki obiekt jest przechowywany w zmiennej klasie, Python zadzwoni do metody __get__ za każdym razem, gdy odpowiednia nazwa jest podnośnikowa na instancji. Należy pamiętać, że to specjalne zachowanie nie dzieje się do obiektów deskryptorów przechowywanych w zmiennych instancji, tylko te, które są zmiennymi klasowymi.

Funkcje są deskryptorami. Oznacza to, że po zapisaniu funkcji jako zmienna klasowa, jego metoda {x0}} zostanie wywołana podczas spojrzenia na instancję. Metoda ta powróci obiekt "metodę związaną", która przejdzie razem wzdłuż argumentu self do funkcji automatycznie.

Jeśli przechowujesz funkcję gdzieś inny niż zmienna klasy najwyższejologicznej (np. W słowniku lub w zmiennej instancji), nie otrzymasz tego związanego zachowania, ponieważ protokół deskryptora nie zostanie wywołany, gdy obiekt będzie wyglądał w górę. Zwykle oznacza to, że potrzebujesz, aby przejść self ręcznie, albo powinieneś pominąć argument self z definicji funkcji w pierwszej kolejności (w którym to przypadku sugeruję przeniesienie funkcji z klasy do Uczyń go jasno, że nie ma być używane jako metoda).

Ale możesz także skonstruować związane metody ręcznie, jeśli chcesz. Typ jest odsłonięty w module types, jako types.MethodType. Więc możesz zmienić taki kod taki jak i powinien pracować:

def __init__(self,func=None):
    if func == None: func=self.default 
    self.run = types.MethodType(self._getActivation(func), self) # be sure to import types
4
Blckknght 24 czerwiec 2017, 22:20

Używasz niezwiązanej metody (nieliniarzałarstep) w tym kodzie:

activationFunctions = {
    'bipolar': nonLinearBipolarStep ,
}

Dłuższa odpowiedź: Metody są funkcjami zdefiniowanymi w korpusie klasowym i zawsze biorą co najmniej jeden argument, tzw. Self (chyba że używasz @staticFunction i przekręć je w normalne funkcje). Self jest przedmiotem danej klasy, na której nazywa się metoda (w ten sposób w C ++). W Pythonie nie ma prawie nic specjalnego o tym argumencie, nie musi być nazwany ja. Teraz, gdy nazywasz niezwiązaną metodą, po pierwszym argumencie, które otrzymałeś, zostanie zinterpretowany jako jaźń i spożywana. Jeśli nazywasz metodami związanymi, to zużycie nie może się zdarzyć (metoda ma już swój obiekt). Na przykład:

class A:
  def foo(self, x): print(x)
a = A()
a.foo(1) # a.foo is bound method, a is self, prints 1
A.foo(a, 2) # A.foo is unbound method, first argument becomes self, prints 2

Aktualizacja: Dlaczego w ogóle działa. Krótka odpowiedź: Ponieważ operator Dot (.) Zaktualizuje niezwiązaną metodę do związanego, gdy może.

Rozważ, co się stanie, kiedy piszesz A.Foo (1). Pierwszy Python Sprawdź obiekt A na Foo i nie znajduje nic (Foo nie jest wartością przypisaną do A). Idzie więc do klasy (o nazwie a) i lo, a oto - foo jest tam i jest używany. Ale tutaj jest sztuczka. Python będzie wiązać obiekt A do niezwiązanej metody A.FOO (szczegóły ucieczki mi teraz, więc wyobraź sobie, że Dragon to zrobił) i uczynić go w związku. Więc A.Foo jest związany i nie potrzebuje już siebie z argumentów, więc 1 idzie do argumentu X i wszystko działa.

Teraz do twojego kodu: Używasz "dwunożarowy": nieliniarzowy w mapie, która jest niezwiązana metodą. Następnie w konstruktorze ( init ) ustawić samodzielnie. W danym przykładzie powróciłeś niezwiązaną metodę nielinibipolarstep i przypisujesz go do self.run. Teraz nazywasz ag.run. Przejście przez logikę z poprzedniego akapitu AG.RUN po raz pierwszy spojrzał na obiekt AG. A oto twój błąd - znaleziono. Jako Python znalazł Ag.run wartość wewnątrz obiektu AG, nigdy nie jest konsultowany typ AG (aktywacja) do uruchomienia obiektu i nigdy nie miał okazji go wiązać. Więc ag.run jest niezwiązany metodą i oczekuj, jak pierwszy argument jako pierwszy.

Ogólniesz dwie opcje. Ag.run (AG, 4), który będzie działać, ale jego brzydka lub ręcznie wiążą metodę siebie w konstruktorze. Ten ostatni możesz to zrobić:

self.run = self._getActivation(func).__get__(self)
1
Radosław Cybulski 25 czerwiec 2017, 17:43