আমি কীভাবে উল্লেখ করতে পারি যে কোনও পদ্ধতির রিটার্নের ধরণটি ক্লাসের মতোই?


410

অজগর 3 এ আমার নিম্নলিখিত কোড রয়েছে:

class Position:

    def __init__(self, x: int, y: int):
        self.x = x
        self.y = y

    def __add__(self, other: Position) -> Position:
        return Position(self.x + other.x, self.y + other.y)

তবে আমার সম্পাদক (পাইচার্ম) বলেছেন যে রেফারেন্স পজিশনটি সমাধান করা যায় না ( __add__পদ্ধতিতে)। আমি কীভাবে উল্লেখ করব যে আমি প্রত্যাবর্তন টাইপ প্রকারের প্রত্যাশা করব Position?

সম্পাদনা: আমি মনে করি এটি আসলে পাইচার্ম ইস্যু। এটি আসলে তার সতর্কতাগুলিতে এবং কোড সমাপ্তির তথ্য ব্যবহার করে

তবে আমি ভুল হলে আমাকে সংশোধন করুন এবং অন্য কিছু বাক্য গঠন ব্যবহার করা দরকার।

উত্তর:


574

টিএল; ডিআর : আপনি যদি পাইথন ৪.০ ব্যবহার করেন তবে এটি কেবল কাজ করে। আজকের (2019) হিসাবে 3.7+ আপনাকে ভবিষ্যতের বিবৃতি ( from __future__ import annotations) ব্যবহার করতে হবে - পাইথন ৩.6 বা তার নীচে একটি স্ট্রিং ব্যবহার করুন।

আমার ধারণা আপনি এই ব্যতিক্রমটি পেয়েছেন:

NameError: name 'Position' is not defined

এটি কারণ Positionআপনি অজানা 4 ব্যবহার না করে আপনি কোনও টীকাটি ব্যবহার করার আগে এটি সংজ্ঞায়িত করতে হবে।

পাইথন ৩.7++: from __future__ import annotations

পাইথন 3.7 পিইপি 563 উপস্থাপন করেছে : টীকাগুলির স্থগিত মূল্যায়ন । ভবিষ্যতের বিবৃতি ব্যবহার করে এমন একটি মডিউল from __future__ import annotationsস্বয়ংক্রিয়ভাবে টীকাগুলি স্ট্রিং হিসাবে সংরক্ষণ করবে:

from __future__ import annotations

class Position:
    def __add__(self, other: Position) -> Position:
        ...

এটি পাইথন ৪.০ এ ডিফল্ট হওয়ার কথা। পাইথন যেহেতু এখনও একটি গতিসম্পন্ন টাইপযুক্ত ভাষা তাই রানটাইমে কোনও প্রকারের চেকিং করা হয় না, টাইপিং টীকাগুলির কোনও কার্যকারিতা প্রভাবিত হওয়া উচিত, তাই না? ভুল! পাইথন ৩.7 এর আগে টাইপিং মডিউলটি মূলতম ধীরে ধীরে পাইথন মডিউলগুলির মধ্যে একটি হিসাবে ব্যবহৃত হত তাই আপনি যদি 3..7-এ উন্নীত হন তবে আপনি যদি পারফরম্যান্সে times গুণ বৃদ্ধি পেতে দেখবেন import typing

পাইথন <3.7: একটি স্ট্রিং ব্যবহার করুন

পিইপি 484 অনুসারে , আপনার নিজের ক্লাসের পরিবর্তে একটি স্ট্রিং ব্যবহার করা উচিত:

class Position:
    ...
    def __add__(self, other: 'Position') -> 'Position':
       ...

আপনি যদি জ্যাঙ্গো কাঠামো ব্যবহার করেন তবে এটি পরিচিত হতে পারে কারণ জ্যাঙ্গো মডেলগুলি ফরোয়ার্ড রেফারেন্সগুলির জন্য স্ট্রিং ব্যবহার করে (বিদেশী মডেল যেখানে বিদেশী মডেল রয়েছে selfবা এখনও তা ঘোষিত হয়নি)। এটি পাইচার্ম এবং অন্যান্য সরঞ্জামগুলির সাথে কাজ করা উচিত।

সোর্স

আপনাকে ভ্রমণের হাতছাড়া করতে পিইপি 484 এবং পিইপি 563 এর প্রাসঙ্গিক অংশগুলি :

ফরোয়ার্ড রেফারেন্স

যখন কোনও প্রকার ইঙ্গিতটিতে এমন নাম থাকে যা এখনও সংজ্ঞায়িত হয়নি, তখন সেই সংজ্ঞাটি স্ট্রিং আক্ষরিক হিসাবে প্রকাশ করা হতে পারে, পরে সমাধান হতে পারে।

একটি পরিস্থিতি যেখানে এটি সাধারণত ঘটে থাকে তা একটি ধারক শ্রেণীর সংজ্ঞা, যেখানে শ্রেণিটি সংজ্ঞায়িত করা হয় কয়েকটি পদ্ধতির স্বাক্ষরে ঘটে। উদাহরণস্বরূপ, নিম্নলিখিত কোড (একটি সাধারণ বাইনারি ট্রি প্রয়োগের শুরু) কাজ করে না:

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

স্ট্রিং আক্ষরিক একটি বৈধ পাইথন এক্সপ্রেশন থাকা উচিত (যেমন, সংকলন (লিট, '', 'ইভাল') একটি বৈধ কোড অবজেক্ট হওয়া উচিত) এবং মডিউলটি সম্পূর্ণ লোড হয়ে গেলে এটি ত্রুটি ছাড়াই মূল্যায়ন করা উচিত। স্থানীয় এবং গ্লোবাল নেমস্পেস যেখানে এটি মূল্যায়ন করা হয় একই নামস্থান হওয়া উচিত যেখানে একই ফাংশনের ডিফল্ট আর্গুমেন্টগুলি মূল্যায়ন করা হত।

এবং পিইপি 563:

পাইথন ৪.০-তে, ফাংশন এবং পরিবর্তনশীল টীকাগুলি আর সংজ্ঞা সময়ে মূল্যায়ন করা হবে না। পরিবর্তে, একটি স্ট্রিং ফর্মটি সংশ্লিষ্ট __annotations__অভিধানে সংরক্ষণ করা হবে । স্ট্যাটিক টাইপ চেকাররা আচরণের মধ্যে কোনও তফাত দেখতে পাবেন না, অন্যদিকে রানটাইম এ টীকাগুলি ব্যবহার করা সরঞ্জামগুলি স্থগিত মূল্যায়ন করতে হবে।

...

উপরে বর্ণিত কার্যকারিতাটি নীচের বিশেষ আমদানি ব্যবহার করে পাইথন ৩.7 থেকে শুরু করে সক্ষম করা যেতে পারে:

from __future__ import annotations

পরিবর্তে যে জিনিসগুলি আপনাকে প্রলোভন করতে পারে

উ: একটি ডামি সংজ্ঞা দিন Position

শ্রেণীর সংজ্ঞার আগে একটি ডামি সংজ্ঞা রাখুন:

class Position(object):
    pass


class Position(object):
    ...

এটি এ থেকে মুক্তি পাবে NameErrorএবং এমনকি ঠিক দেখাবে:

>>> Position.__add__.__annotations__
{'other': __main__.Position, 'return': __main__.Position}

তবে কি তাই?

>>> for k, v in Position.__add__.__annotations__.items():
...     print(k, 'is Position:', v is Position)                                                                                                                                                                                                                  
return is Position: False
other is Position: False

বি। টীকা যুক্ত করার জন্য বানর-প্যাচ:

আপনি কিছু পাইথন মেটা প্রোগ্রামিং ম্যাজিক চেষ্টা করতে পারেন এবং টীকা যুক্ত করতে ক্লাসের সংজ্ঞাটি বানর-প্যাচে একটি ডেকরেটার লিখতে পারেন:

class Position:
    ...
    def __add__(self, other):
        return self.__class__(self.x + other.x, self.y + other.y)

সাজসজ্জার এর সমপরিমাণের জন্য দায়বদ্ধ হওয়া উচিত:

Position.__add__.__annotations__['return'] = Position
Position.__add__.__annotations__['other'] = Position

কমপক্ষে এটি সঠিক বলে মনে হচ্ছে:

>>> for k, v in Position.__add__.__annotations__.items():
...     print(k, 'is Position:', v is Position)                                                                                                                                                                                                                  
return is Position: True
other is Position: True

সম্ভবত খুব বেশি ঝামেলা হয়েছে।

উপসংহার

আপনি যদি 3.6 বা নীচে ব্যবহার করছেন তবে ক্লাসের নাম সম্বলিত একটি স্ট্রিং আক্ষরিক ব্যবহার করুন, 3.7 ব্যবহার করে from __future__ import annotationsএবং এটি কেবল কাজ করবে।


2
ঠিক আছে, এটি একটি পাইচার্ম ইস্যু কম এবং পাইথন 3.5 পিইপি 484 ইস্যু। আমার সন্দেহ হয় আপনি যদি মাইপি টাইপ টুলটির মাধ্যমে চালনা করেন তবে আপনি একই সতর্কতাটি পেয়ে যাবেন।
পল এভারিট

23
> আপনি যদি পাইথন ৪.০ ব্যবহার করেন তবে এটি কেবল পথে কাজ করে, আপনি কি সারা কনরকে দেখেছেন? :)
স্ক্রুতারী

@ জোয়েল বার্কলে আমি মাত্র এটি পরীক্ষা করেছি এবং টাইপ পরামিতিগুলি আমার জন্য 3.6-তে কাজ করেছে, typingস্ট্রিংয়ের মূল্যায়ন করার সময় আপনি যে কোনও ধরণের ব্যবহার করেন তা অবশ্যই সুযোগের মধ্যে থাকতে হবে বলে আমদানি করতে ভুলবেন না ।
পাওলো স্কার্ডাইন

আহা, আমার ভুল, আমি কেবল ''ক্লাসটি
ঘুরছিলাম

5
যে কেউ ব্যবহার করছে তার জন্য গুরুত্বপূর্ণ নোট from __future__ import annotations- অন্যান্য সমস্ত আমদানির আগে এটি অবশ্যই আমদানি করা উচিত।
আর্তুর

16

প্রকারটি স্ট্রিং হিসাবে উল্লেখ করা ভাল, তবে সর্বদা আমাকে কিছুটা কৃতজ্ঞ করে তোলে যে আমরা মূলত পার্সারটিকে ছুঁড়ে ফেলছি। সুতরাং আপনি এই আক্ষরিক স্ট্রিংগুলির একটিরও ভুল বানান না করাই ভাল:

def __add__(self, other: 'Position') -> 'Position':
    return Position(self.x + other.x, self.y + other.y)

একটি সামান্য প্রকরণটি একটি সীমাবদ্ধ টাইপভার ব্যবহার করা হয়, কমপক্ষে তারপরে টাইপভার ঘোষণার সময় আপনাকে কেবল একবার স্ট্রিংটি লিখতে হবে:

from typing import TypeVar

T = TypeVar('T', bound='Position')

class Position:

    def __init__(self, x: int, y: int):
        self.x = x
        self.y = y

    def __add__(self, other: T) -> T:
        return Position(self.x + other.x, self.y + other.y)

8
আমি আশা করি পাইথনের একটি typing.Selfস্পষ্টভাবে এটি নির্দিষ্ট করতে পারে।
আলেকজান্ডার হুজ্জাহ

2
আপনার এখানে কিছু আছে কি না তা দেখতে আমি এখানে এসেছি typing.Self। পলিওরফিজমটি লাভারেজ করার সময় একটি শক্ত কোডড স্ট্রিং ফিরিয়ে দেওয়া সঠিক ধরণের ফিরতে ব্যর্থ হয়। আমার ক্ষেত্রে আমি বাস্তবায়ন করতে চেয়েছিলেন deserialize classmethod। আমি ডিক (কাওয়ার্গস) ফিরিয়ে ডাকতে স্থির হয়েছি some_class(**some_class.deserialize(raw_data))
স্কট পি।

সাবক্ল্যাস ব্যবহার করার জন্য এটি সঠিকভাবে প্রয়োগ করার সময় এখানে ব্যবহৃত ধরণের টিকাগুলি উপযুক্ত। যাইহোক, বাস্তবায়ন Positionক্লাস নয়, ফিরে আসে , সুতরাং উপরের উদাহরণটি প্রযুক্তিগতভাবে ভুল। বাস্তবায়ন Position(যেমন কিছু সঙ্গে প্রতিস্থাপন করা উচিত self.__class__(
স্যাম বুল

অতিরিক্তভাবে, টীকাগুলি বলে যে রিটার্নের ধরণের উপর নির্ভর করে otherতবে সম্ভবত এটি নির্ভর করে self। সুতরাং, selfসঠিক আচরণ বর্ণনা করার জন্য আপনাকে এন্টোটেশনটি লাগাতে হবে (এবং এটি কেবল রিটার্নের ধরণের সাথে আবদ্ধ নয় তা দেখানো otherউচিত Position)। আপনি কেবলমাত্র কাজ করছেন এমন ক্ষেত্রেও এটি ব্যবহার করা যেতে পারে self। যেমনdef __aenter__(self: T) -> T:
স্যাম বুল

15

শ্রেণীর বডি নিজেই বিশ্লেষণের সময় 'পজিশন' নামটি উপলভ্য নয়। আপনি কীভাবে প্রকারের ঘোষণাগুলি ব্যবহার করছেন তা আমি জানি না, তবে পাইথনের পিইপি 484 - এই টাইপিং ইঙ্গিতগুলি ব্যবহার করে যদি সবচেয়ে মোড ব্যবহার করা উচিত তবে আপনি নামটি কেবলমাত্র এই পংক্তিতে স্ট্রিং হিসাবে রাখতে পারেন:

def __add__(self, other: 'Position') -> 'Position':
    return Position(self.x + other.x, self.y + other.y)

পরীক্ষা করে দেখুন https://www.python.org/dev/peps/pep-0484/#forward-references - যে অনুসারী সরঞ্জাম সেখান থেকে বর্গ নাম এবং তা করা ব্যবহার মোড়ক খোলা জানতে হবে (এটা সবসময় আছে গুরুত্বপূর্ণ। মনে রাখবেন যে পাইথন ভাষা নিজেই এই টীকাগুলির কিছুই করে না - এগুলি সাধারণত স্থির-কোড বিশ্লেষণের জন্য বোঝানো হয়, বা রান-টাইমে টাইপ পরীক্ষার জন্য একটি লাইব্রেরি / কাঠামো থাকতে পারে - তবে আপনাকে এটি স্পষ্টভাবে সেট করতে হবে)।

আপডেট এছাড়াও, পাইথন ৩.৮ হিসাবে, পেপ-563৩ পরীক্ষা করুন - পাইথন ৩.৮ হিসাবে from __future__ import annotationsটীকাগুলির মূল্যায়ন স্থগিত করতে লিখতে সম্ভব - ফরোয়ার্ড রেফারেন্সিং ক্লাসগুলি সোজাভাবে কাজ করা উচিত।


9

যখন স্ট্রিং-ভিত্তিক ধরণের ইঙ্গিতটি গ্রহণযোগ্য হয়, তখন __qualname__আইটেমটি ব্যবহার করা যায়। এটি শ্রেণীর নাম ধারণ করে এবং এটি শ্রেণীর সংজ্ঞার মূল শৃঙ্গে পাওয়া যায়।

class MyClass:
    @classmethod
    def make_new(cls) -> __qualname__:
        return cls()

এটি করে ক্লাসের নাম পরিবর্তন করে হিন্ট টাইপের সংশোধন করা বোঝায় না। তবে আমি ব্যক্তিগতভাবে স্মার্ট কোড সম্পাদকরা এই ফর্মটি ভালভাবে পরিচালনা করবে বলে আশা করব না।


1
এটি বিশেষত কার্যকর কারণ এটি শ্রেণীর নামটিকে হার্ডকোড দেয় না, তাই এটি সাবক্লাসে কাজ করে চলে।
ফ্লোরিয়ান ব্রুকার

আমি এ্যানোটেশনগুলির স্থগিত মূল্যায়নের (পিইপি 563) কাজ করবে কিনা তা নিশ্চিত নই, তাই আমি এর জন্য একটি প্রশ্ন জিজ্ঞাসা করেছি ।
ফ্লোরিয়ান ব্রুকার
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.