Czy miksy Pythona są anty-wzorcem?

34

W pełni zdaję sobie sprawę, że pylinti inne narzędzia analizy statycznej nie są wszechwiedzące, a czasem ich radom należy się nie posłuchać. (Dotyczy to różnych klas wiadomości, nie tylko conventions.)


Jeśli mam zajęcia takie jak

class related_methods():

    def a_method(self):
        self.stack.function(self.my_var)

class more_methods():

    def b_method(self):
        self.otherfunc()

class implement_methods(related_methods, more_methods):

    def __init__(self):
        self.stack  = some()
        self.my_var = other()

    def otherfunc(self):
        self.a_method()

Oczywiście jest to wymyślone. Oto lepszy przykład, jeśli chcesz .

Uważam, że ten styl nazywa się przy użyciu „miksów”.

Podobnie jak inne narzędzia, pylintocenia ten kod na -21.67 / 10, przede wszystkim dlatego, że myśli more_methodsi related_methodsnie ma selfatrybutów otherfunc, a także dlatego stack, my_varże bez uruchomienia kodu najwyraźniej nie widzi related_methodsi nie more_methodsjest w to zamieszany implement_methods.

Kompilatory i narzędzia do analizy statycznej nie zawsze mogą rozwiązać problem zatrzymania , ale uważam, że jest to z pewnością przypadek, w którym sprawdzenie, co odziedziczyłoby implement_methods, pokazałoby, że jest to całkowicie poprawne, i to byłoby bardzo łatwe.

Dlaczego narzędzia analizy statycznej odrzucają ten prawidłowy (myślę) wzór OOP?

Zarówno:

  1. Nie próbują nawet sprawdzać dziedziczenia lub

  2. mixiny są odradzane w idiomatycznym, czytelnym Pythonie


# 1 jest oczywiście niepoprawny, ponieważ jeśli poproszę pylinto powiedzenie mi o klasie, która dziedziczy, unittest.TestCaseże używa self.assertEqual((coś zdefiniowanego tylko w unittest.TestCase)), to nie narzeka.

Czy miksy są niepytoniczne czy zniechęcone?

kot
źródło
1
Czy to złe pytanie? Czy jest nie na temat? czy nie da się odpowiedzieć? Czy jestem głupi? Ludzie mogą DV, ale nie chcą pomóc w ulepszaniu.
kot
1
Są tacy, którzy prawdopodobnie są zmęczeni tym, że ludzie pytają „czy to (anty-) wzorzec” lub „czy to (nie) python”.
9
@MichaelT W porządku. Mogą wtedy przestać sprawdzać pytania.
Katana314,
2
@tac ludzie będą głosować z różnych powodów, ale nigdy nie muszą wyjaśniać, dlaczego - przycisk głosowania ma podpowiedź, która mówi: „to pytanie nie pokazuje żadnego wysiłku badawczego; jest niejasne lub nieużyteczne” - niejasne, jeśli akceptowalna odpowiedź. 8 w górę i 1 w dół jest w rzeczywistości całkiem niezłe, szczególnie w przypadku pytania, w którym downvotes jest bezpłatne. Nie pozwól, żeby cię to przygnębiło. Twoje zdrowie.
Aaron Hall,
@AaronHall Wiem, że ludzie mogą robić to, co im się podoba, oddając swój głos. Rzeczywiście mówię nowym użytkownikom, którzy narzekają na to samo.
kot

Odpowiedzi:

16

Mixiny po prostu nie są przypadkiem użycia rozważanym przez narzędzie. To nie znaczy, że niekoniecznie jest to zły przypadek użycia, po prostu rzadki dla Pythona.

To, czy miksy są odpowiednio stosowane w konkretnym przypadku, to inna sprawa. Anty-wzorzec mixinowy, który widzę najczęściej, używa mixin, gdy kiedykolwiek ma być tylko jedna kombinacja mieszana. To tylko okrągły sposób na ukrycie klasy boga. Jeśli nie można myśleć o przyczyny tej chwili zamienić się lub opuścić jedną z wstawek, nie powinno być wstawek.

Karl Bielefeldt
źródło
tak, to obiekt boski, przyznaję. W takim przypadku argumentowałbym, że procesor może być bogiem.
kot
15

Wierzę, że Mixiny mogą być absolutnie Pythonic. Jednak idiomatycznym sposobem wyciszenia linera - i poprawienia czytelności Mixinów - jest zarówno (1) zdefiniowanie metod abstrakcyjnych, które jawnie określają metody, które dzieci Mixina muszą wdrożyć, oraz (2) wstępne zdefiniowanie None-wartościowe pola dla członków danych Mixin, które dzieci muszą zainicjować.

Zastosowanie tego wzoru do Twojego przykładu:

from abc import ABC, abstractmethod


class related_methods():
    my_var = None
    stack = None

    def a_method(self):
        self.stack.function(self.my_var)


class more_methods(ABC):
    @abstractmethod
    def otherfunc(self):
        pass

    def b_method(self):
        self.otherfunc()


class implement_methods(related_methods, more_methods):
    def __init__(self):
        self.stack  = some()
        self.my_var = other()

    def otherfunc(self):
        self.a_method()
UltraBird
źródło
2
+1 za twoją odpowiedź, chociaż abstract other(self): passrzecz zamienia zamieszanie liniowca na moje
kot
To jeszcze bardziej mylące bez @abstractmethod ;-) Zacząłem od Javy, więc jest to dla mnie w porządku.
Chris Huang-Leaver,
9

Myślę, że mixiny mogą być dobre, ale myślę, że pylint ma rację w tym przypadku. Zastrzeżenie: następujące rzeczy oparte na opiniach.

Dobra klasa, w tym mixiny, ma jedną wyraźną odpowiedzialność. Idealnie, mixin powinien przenosić cały stan, do którego ma dostęp, i logikę do obsługi. Np. Dobry mixin może dodać last_updatedpole do klasy modelu ORM, zapewnić logikę do jego ustawiania oraz metody wyszukiwania najstarszego / najnowszego rekordu.

Odwoływanie się do niezadeklarowanych członków instancji (zmiennych i metod) wygląda trochę dziwnie.

Właściwe podejście bardzo zależy od danego zadania.

Może to być mixin z zachowanym odpowiednim bitem stanu.

Może to być inna hierarchia klas, w której metody, które obecnie rozpowszechniasz za pomocą mixinu, należą do klasy podstawowej, podczas gdy różnice w implementacji niższego poziomu należą do podklas. Wygląda to najbardziej pasujące do twojego przypadku z operacjami stosu.

Może to być dekorator klasy, który dodaje metodę lub dwie; zwykle ma to sens, gdy trzeba przekazać argumentowi dekoratorowi, aby wpłynąć na generowanie metody.

Podaj swój problem, wyjaśnij swoje większe obawy dotyczące projektu, a następnie możemy spierać się, czy w Twoim przypadku coś jest anty-wzorem.

9000
źródło
6

Linter nie wie, że używasz klasy jako mixin. Pylint jest świadomy, że używasz mixinu, jeśli dodasz sufiks „mixin” lub „Mixin” na końcu nazwy klasy, wówczas linijka przestaje narzekać.

linter_with_mixins linter2_with_mixin

Mixiny nie są złe ani dobre same w sobie, są tylko narzędziem. Sprawiasz, że są dobre lub złe.

dotoscat
źródło
2
brzmi to bardziej jak komentarz, patrz Jak odpowiedzieć
gnat
2
jest to cenna odpowiedź, ponieważ nie sądzę, bym kiedykolwiek wiedział o tej podpowiedzi inaczej
cat
1

Czy mixiny są w porządku?

Czy miksy Pythona są anty-wzorcem?

Mixiny nie są odradzane - stanowią dobry przypadek wielokrotnego dziedziczenia.

Dlaczego twój linter narzeka?

Pylint skarży się oczywiście, bo nie wiem gdzie otherfunc, stacki my_var pochodzi.

Trywialny?

Nie ma od razu widocznego dobrego powodu, abyś podzielił te dwie metody na osobne klasy nadrzędne, ani w twoim przykładzie w pytaniu, ani w bardziej trywialnym połączonym przykładzie, pokazanym tutaj.

201
202 class OpCore():
203     # nothing runs without these
204
205 class OpLogik(): 
206     # logic methods... 
207 
208 class OpString(): 
209     # string things
210 
211 
212 class Stack(OpCore, OpLogik, OpString): 
213 
214     "the mixin mixer of the above mixins" 

Koszty miksów i hałasu

Koszty związane z tym, co robisz, powodują, że Twój linter zgłasza hałas. Ten hałas może zasłaniać ważniejsze problemy z kodem. Ważenie to ważny koszt. Kolejnym kosztem jest to, że dzielisz swój powiązany kod na różne przestrzenie nazw, co może utrudnić programistom odkrycie znaczenia.

Wniosek

Dziedziczenie pozwala na ponowne użycie kodu. Jeśli ponownie wykorzystasz kod ze swoimi miksami, to świetnie, stworzyły one dla ciebie wartość, która prawdopodobnie przewyższa ewentualne inne koszty. Jeśli nie otrzymujesz ponownego użycia / deduplikacji kodu linii kodu, prawdopodobnie nie zyskujesz zbyt wiele na swoich miksach, i w tym momencie myślę, że koszt hałasu jest większy niż korzyści.

Aaron Hall
źródło
Istnieje dobry powód, aby oddzielać je od siebie w powiązanym, nietrywialnym przykładzie - łatwiej mi zorganizować kod, gdy mam ułamek pełnej klasy, która implementuje matematykę, innej, która implementuje ciągi znaków itd., A ty je mieszasz dostać Forth Soup.
kot
„Kolejnym kosztem jest to, że dzielisz kod na różne przestrzenie nazw, co może utrudniać programistom odkrycie znaczenia” - nie, wcale tak nie jest. Przestrzenie nazw klas ułatwiają stwierdzenie, co robi grupa metod, a gdy ktoś chce użyć stosu, powinien wywołać / wdrożyć zupę Forth, a nie poszczególne składniki
cat
@cat prawdopodobnie dlatego, że są zaprojektowane i nie są używane w prawdziwym dużym projekcie, żaden przykład tak naprawdę nie obsługuje twoich punktów za używanie mixin. Oba pokazują tylko jeden raz użycie mixinu, w którym to przypadku umieszczenie implementacji w jednym miejscu jest lepszym stylem i lepszą organizacją. Teraz, jeśli możesz pokazać przypadek, w którym chcesz mieć wspólne funkcje w różnych klasach, ale nie chcesz wspólnej klasy podstawowej, to w tym miejscu możesz użyć mixinu. Który jest obok twojego kłopotliwego pytania (UltraBird odpowiedział na to dobrze). Ale to ogólnie odpowiada na twoje pytanie dotyczące zastosowania mixin.
dlamblin