Próbuję podzielić moją ogromną klasę na dwie; cóż, w zasadzie do klasy "main" i mieszanki z dodatkowymi funkcjami, takimi jak:
main.py
plik:
import mymixin.py
class Main(object, MyMixin):
def func1(self, xxx):
...
mymixin.py
plik:
class MyMixin(object):
def func2(self: Main, xxx): # <--- note the type hint
...
Chociaż działa to dobrze, podpowiedź MyMixin.func2
dotycząca typu oczywiście nie może działać. Nie mogę importować main.py
, ponieważ otrzymywałbym import cykliczny i bez podpowiedzi mój redaktor (PyCharm) nie może powiedzieć, co self
jest.
Używam Pythona 3.4 i chcę przejść na 3.5, jeśli jest tam dostępne rozwiązanie.
Czy jest jakiś sposób, żebym mógł podzielić moją klasę na dwa pliki i zachować wszystkie „połączenia”, aby moje IDE nadal oferowało mi automatyczne uzupełnianie i wszystkie inne korzyści, które z niego pochodzą, znając typy?
self
, ponieważ zawsze będzie to podklasa bieżącej klasy (a każdy system sprawdzania typu powinien być w stanie samodzielnie to rozgryźć). Czyfunc2
próby wywołaniafunc1
, które nie są zdefiniowane wMyMixin
? Może powinno być (jakoabstractmethod
, może)?class Main(MyMixin, SomeBaseClass)
aby metody z bardziej szczegółowej klasy mogły przesłonić te z klasy bazowejOdpowiedzi:
Obawiam się, że generalnie nie ma niezwykle eleganckiego sposobu obsługi cykli importu. Masz do wyboru przeprojektowanie kodu, aby usunąć cykliczną zależność, lub jeśli nie jest to wykonalne, zrób coś takiego:
# some_file.py from typing import TYPE_CHECKING if TYPE_CHECKING: from main import Main class MyObject(object): def func2(self, some_param: 'Main'): ...
TYPE_CHECKING
Stała jest zawszeFalse
przy starcie, więc import nie będą oceniane, ale mypy (i innych narzędzi typu sprawdzanie) oceni zawartość tego bloku.Musimy również przekształcić
Main
adnotację typu w ciąg, skutecznie deklarując go do przodu, ponieważMain
symbol nie jest dostępny w czasie wykonywania.Jeśli używasz Pythona 3.7+, możemy przynajmniej pominąć konieczność podawania wyraźnej adnotacji w postaci ciągu, korzystając z PEP 563 :
# some_file.py from __future__ import annotations from typing import TYPE_CHECKING if TYPE_CHECKING: from main import Main class MyObject(object): # Hooray, cleaner annotations! def func2(self, some_param: Main): ...
from __future__ import annotations
Import uczyni wszystkie podpowiedzi typu BE strun i pominąć ich oceny. Może to pomóc uczynić nasz kod nieco bardziej ergonomicznym.Wszystko to powiedziawszy, używanie mixinów z myPy prawdopodobnie będzie wymagało nieco większej struktury niż obecnie. Mypy zaleca podejście, które jest w zasadzie tym, co
deceze
opisuje - stworzenie ABC, które dziedziczy zarówno twoja, jakMain
iMyMixin
klasy. Nie zdziwiłbym się, gdybyś musiał zrobić coś podobnego, aby uszczęśliwić pycharma.źródło
typing
, ale PyCharm również był z niego zadowolonyif False:
.__init__
typing. TYPE_CHECKING
: python.org/dev/peps/pep-0484/#runtime-or-type-checkingDla osób borykających się z cyklicznymi importami podczas importowania klas tylko do sprawdzania typu: prawdopodobnie będziesz chciał użyć odniesienia do przodu (PEP 484 - Wskazówki dotyczące typu):
Więc zamiast:
class Tree: def __init__(self, left: Tree, right: Tree): self.left = left self.right = right
ty robisz:
class Tree: def __init__(self, left: 'Tree', right: 'Tree'): self.left = left self.right = right
źródło
File -> Invalidate Caches
?if False:
ciebie możesz teżfrom typing import TYPE_CHECKING
iif TYPE_CHECKING:
.Większym problemem jest to, że twoje typy nie są rozsądne na początku.
MyMixin
przyjmuje zakodowane na sztywno założenie, że zostanie wmieszanyMain
, podczas gdy może zostać zmieszany z dowolną liczbą innych klas, w którym to przypadku prawdopodobnie się zepsuje. Jeśli twój mixin jest zakodowany na sztywno, aby był mieszany w jedną konkretną klasę, równie dobrze możesz napisać metody bezpośrednio do tej klasy, zamiast je rozdzielać.Aby poprawnie zrobić to za pomocą rozsądnego pisania,
MyMixin
należy zakodować w interfejsie lub klasie abstrakcyjnej w języku Pythona:import abc class MixinDependencyInterface(abc.ABC): @abc.abstractmethod def foo(self): pass class MyMixin: def func2(self: MixinDependencyInterface, xxx): self.foo() # ← mixin only depends on the interface class Main(MixinDependencyInterface, MyMixin): def foo(self): print('bar')
źródło
Okazuje się, że moja pierwotna próba również była dość bliska rozwiązania. Oto, czego obecnie używam:
# main.py import mymixin.py class Main(object, MyMixin): def func1(self, xxx): ... # mymixin.py if False: from main import Main class MyMixin(object): def func2(self: 'Main', xxx): # <--- note the type hint ...
Zwróć uwagę na instrukcję import within
if False
, która nigdy nie jest importowana (ale IDE i tak wie o tym) i używaMain
klasy jako ciągu znaków, ponieważ nie jest ona znana w czasie wykonywania.źródło
Myślę, że idealnym sposobem powinno być zaimportowanie wszystkich klas i zależności w pliku (jak
__init__.py
), a następniefrom __init__ import *
we wszystkich innych plikach.W tym przypadku jesteś
źródło
import *
, a mimo to możesz skorzystać z tego prostego podejścia