साइकल आयात के बिना अजगर प्रकार संकेत


108

मैं अपने विशाल वर्ग को दो में विभाजित करने की कोशिश कर रहा हूं; ठीक है, मूल रूप से "मुख्य" वर्ग और अतिरिक्त कार्यों के साथ एक मिश्रण में, जैसे:

main.py फ़ाइल:

import mymixin.py

class Main(object, MyMixin):
    def func1(self, xxx):
        ...

mymixin.py फ़ाइल:

class MyMixin(object):
    def func2(self: Main, xxx):  # <--- note the type hint
        ...

अब, जबकि यह ठीक काम करता है, प्रकार संकेत MyMixin.func2निश्चित रूप से काम नहीं कर सकता है। मैं आयात नहीं कर सकता main.py, क्योंकि मुझे एक चक्रीय आयात मिलेगा और संकेत के बिना, मेरा संपादक (PyCharm) यह नहीं बता सकता कि क्याself है।

मैं पायथन 3.4 का उपयोग कर रहा हूं, अगर कोई समाधान उपलब्ध है तो 3.5 पर जाने के लिए तैयार है।

क्या कोई तरीका है जिससे मैं अपनी कक्षा को दो फ़ाइलों में विभाजित कर सकता हूँ और सभी "कनेक्शन" रख सकता हूँ ताकि मेरी IDE अभी भी मुझे ऑटो पूर्णता प्रदान करे और अन्य सभी प्रकार के उपहार जो इसके प्रकारों को जानते हैं?


2
मुझे नहीं लगता कि आपको आम तौर पर इसके प्रकार की व्याख्या करने की आवश्यकता है self, क्योंकि यह हमेशा वर्तमान वर्ग का एक उपवर्ग होने जा रहा है (और किसी भी प्रकार की जाँच प्रणाली को यह पता लगाने में सक्षम होना चाहिए)। है func2कॉल करने की कोशिश कर func1, जिसमें से परिभाषित नहीं है MyMixin? शायद यह होना चाहिए (एक के रूप में abstractmethod, शायद)?
ब्लेककनथ

यह भी ध्यान दें कि आम तौर पर अधिक-विशिष्ट वर्ग (जैसे आपका मिक्सिन) को कक्षा की परिभाषा में आधार कक्षाओं के बाईं ओर जाना चाहिए, class Main(MyMixin, SomeBaseClass)ताकि अधिक-विशिष्ट वर्ग की विधियां आधार वर्ग से लोगों को ओवरराइड कर सकें
एनेंट्रोपिक

3
मुझे यकीन नहीं है कि ये टिप्पणियां कैसे उपयोगी हैं, क्योंकि वे पूछे जाने वाले प्रश्न के लिए स्पर्शरेखा हैं। वेलिस कोड समीक्षा के लिए नहीं कह रहा था।
जैकब ली

आयातित वर्ग विधियों के साथ अजगर प्रकार संकेत आपकी समस्या का एक सुरुचिपूर्ण समाधान प्रदान करता है।
बेन मार्स

जवाबों:


166

सामान्य तौर पर आयात चक्रों को संभालने के लिए बहुत ही सुंदर तरीका नहीं है, मुझे डर है। आपकी पसंद या तो चक्रीय निर्भरता को हटाने के लिए आपके कोड को फिर से डिज़ाइन करने के लिए है, या यदि यह संभव नहीं है, तो ऐसा कुछ करें:

# 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निरंतर हमेशा होता है Falseतो आयात मूल्यांकन नहीं किया जाएगा, लेकिन mypy (और अन्य प्रकार की जाँच उपकरण) है कि ब्लॉक की सामग्री का मूल्यांकन करेंगे रनटाइम पर,।

हमें Mainएक स्ट्रिंग में प्रकार एनोटेशन बनाने की भी आवश्यकता है , प्रभावी रूप से इसे घोषित करने की आवश्यकता है क्योंकि Mainप्रतीक रनटाइम पर उपलब्ध नहीं है।

आप अजगर 3.7+ का उपयोग कर रहे हैं, तो हम कम से कम का फायदा उठाते हुए एक स्पष्ट स्ट्रिंग एनोटेशन प्रदान करने के लिए होने को छोड़ सकते हैं पीईपी 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आयात कर देगा सभी तार हो प्रकार संकेत और उन्हें मूल्यांकन करने को छोड़ दें। यह हमारे कोड को हल्के से अधिक एर्गोनोमिक बनाने में मदद कर सकता है।

उस सभी ने कहा, मिक्सी के साथ मिक्सी का उपयोग करने के लिए आपको थोड़ी अधिक संरचना की आवश्यकता होगी। Mypy एक दृष्टिकोण की सिफारिश करता है जो मूल रूप decezeसे वर्णन कर रहा है - एक एबीसी बनाने के लिए जो आपके Mainऔर MyMixinवर्गों दोनों को विरासत में मिलता है। मुझे आश्चर्य नहीं होगा अगर आपने पाइक्मर्स चेकर को खुश करने के लिए कुछ ऐसा ही करने की आवश्यकता समाप्त कर दी।


3
इसके लिए धन्यवाद। मेरे वर्तमान अजगर 3.4 नहीं है typing, लेकिन साथ if False:ही साथ PyCharm काफी खुश था ।
वेलिस

एकमात्र समस्या यह है कि यह MyObject को एक Django मॉडल के रूप में नहीं पहचानता है। इस प्रकार और उदाहरण के बारे में __init__
नागों को

यहाँ के लिए इसी स्फूर्ति है typing. TYPE_CHECKING : python.org/dev/peps/pep-0484/#runtime-or-type-checking
Conchylicultor

24

केवल टाइप जाँच के लिए वर्ग आयात करते समय चक्रीय आयात से जूझ रहे लोगों के लिए: आप संभवतः एक फॉरवर्ड रेफरेंस (PEP 484 - टाइप संकेत) का उपयोग करना चाहेंगे :

जब एक प्रकार के संकेत में ऐसे नाम होते हैं जिन्हें अभी तक परिभाषित नहीं किया गया है, तो उस परिभाषा को एक स्ट्रिंग शाब्दिक के रूप में व्यक्त किया जा सकता है, जिसे बाद में हल किया जा सकता है।

इसलिए इसके बजाय:

class Tree:
    def __init__(self, left: Tree, right: Tree):
        self.left = left
        self.right = right

तुम करो:

class Tree:
    def __init__(self, left: 'Tree', right: 'Tree'):
        self.left = left
        self.right = right

हो सकता है PyCharm। क्या आप नवीनतम संस्करण का उपयोग कर रहे हैं? क्या आपने कोशिश की है File -> Invalidate Caches?
टॉमस बर्टकोविएक

धन्यवाद। क्षमा करें, मैंने अपनी टिप्पणी हटा दी थी। यह उल्लेख किया था कि यह काम करता है, लेकिन PyCharm शिकायत करता है। मैंने वेलिस द्वारा सुझाए गए फाल्स हैक का उपयोग करके हल किया । कैश को अमान्य करने से इसका समाधान नहीं हुआ। यह शायद एक PyCharm मुद्दा है।
जैकब ली

1
@ JacobLee के बजाय if False:आप भी कर सकते हैं from typing import TYPE_CHECKINGऔर if TYPE_CHECKING:
भाग्योदयकाल

11

बड़ा मुद्दा यह है कि आपके प्रकार शुरू करने के लिए समझदार नहीं हैं। MyMixinएक हार्डकोड बनाता है कि यह मिश्रित हो जाएगाMain , जबकि इसे किसी भी अन्य वर्गों में मिलाया जा सकता है, जिस स्थिति में यह संभवतः टूट जाएगा। यदि आपका मिक्सकिन एक विशिष्ट वर्ग में मिश्रित करने के लिए हार्डकोड किया गया है, तो आप उन्हें अलग करने के बजाय सीधे तरीके से उस कक्षा में लिख सकते हैं।

ठीक से टाइपिंग के साथ ऐसा करने के लिए , पायथन पार्लरों में MyMixinएक इंटरफ़ेस या सार वर्ग के खिलाफ कोडित किया जाना चाहिए :

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')

1
खैर, मैं यह नहीं कह रहा हूं कि मेरा समाधान महान है। यह वही है जो मैं कोड को अधिक प्रबंधनीय बनाने के लिए करने का प्रयास कर रहा हूं। आपके सुझाव पारित हो सकता है, लेकिन यह वास्तव में सिर्फ मेरी में इंटरफ़ेस करने के लिए पूरे मुख्य वर्ग चलती का मतलब होगा विशिष्ट मामले।
वेलिस

3

बाहर मुड़ता है मेरा मूल प्रयास समाधान के काफी करीब था। यह मैं वर्तमान में उपयोग कर रहा हूं:

# 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
        ...

आयात के if Falseकथन पर ध्यान दें जो कभी भी आयात नहीं किया जाता है (लेकिन IDE इसके बारे में वैसे भी जानता है) और का उपयोग करMain कक्षा को स्ट्रिंग के रूप में क्योंकि यह रनटाइम पर ज्ञात नहीं है।


मुझे उम्मीद है कि यह मृत कोड के बारे में एक चेतावनी का कारण होगा।
फिल

@ फिल: हाँ, उस समय जब मैं पायथन 3.4 का उपयोग कर रहा था। अब टाइपिंग है। YPE_CHECKING
velis

-4

मुझे लगता है कि एक फ़ाइल (जैसे __init__.py) और फिर from __init__ import *अन्य सभी फाइलों में सभी वर्गों और निर्भरता को आयात करने का सही तरीका होना चाहिए ।

इस मामले में आप हैं

  1. उन फ़ाइलों और कक्षाओं के लिए कई संदर्भों से बचना और
  2. यह भी केवल अन्य फ़ाइलों में से प्रत्येक में एक लाइन जोड़ने के लिए है और
  3. तीसरा उन सभी वर्गों के बारे में जानना होगा जो आप उपयोग कर सकते हैं।

1
इसका मतलब है कि आप हर जगह लोडिंग कर रहे हैं, यदि आप एक बहुत भारी पुस्तकालय कर रहे हैं तो इसका मतलब है कि हर आयात के लिए आपको पूरी लाइब्रेरी लोड करने की आवश्यकता है। + संदर्भ सुपर धीमी गति से काम करेगा।
ओमेर शेखम

> इसका मतलब है कि आप हर जगह हर लोडिंग कर रहे हैं। >>>> बिल्कुल नहीं अगर आपके पास " init .py" या अन्य फ़ाइलों में से कई हैं, और बचें import *, और फिर भी आप इस आसान दृष्टिकोण का लाभ उठा सकते हैं
स्लावोमिर लेनर्ट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.