__Get__ এবং __set__ এবং পাইথন বর্ণনাকারী বোঝা


310

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

class Celsius(object):
    def __init__(self, value=0.0):
        self.value = float(value)
    def __get__(self, instance, owner):
        return self.value
    def __set__(self, instance, value):
        self.value = float(value)


class Temperature(object):
    celsius = Celsius()
  1. আমার কেন বর্ণনাকারী শ্রেণি দরকার?

  2. কি instanceএবং ownerএখানে? (ইন __get__) এই পরামিতিগুলির উদ্দেশ্য কী?

  3. এই উদাহরণটি আমি কীভাবে কল / ব্যবহার করব?

উত্তর:


147

বর্ণনাকারী হলেন কীভাবে পাইথনের ধরণটি propertyপ্রয়োগ করা হয়। একজন বর্ণনাকারী কেবল কার্যকরী __get__, __set__ইত্যাদি এবং তারপর (আপনি তাপমাত্রা বর্গ সঙ্গে উপরে করেনি হিসাবে) এর সংজ্ঞা অন্য বর্গ যোগ করা হয়। উদাহরণ স্বরূপ:

temp=Temperature()
temp.celsius #calls celsius.__get__

আপনি বর্ণনাকারীকে নির্ধারিত সম্পত্তিটিতে অ্যাক্সেস করা ( celsiusউপরের উদাহরণে) উপযুক্ত বর্ণনাকারী পদ্ধতিটিকে কল করে।

instanceমধ্যে __get__ক্লাসের নিদর্শনের সঙ্গে হয় (তাই সর্বোপরি, __get__পাবে temp, যখন ownerবর্ণনাকারী সঙ্গে শ্রেণী (তাই এটি হবে Temperature)।

যুক্তিটি যেটিকে শক্তিশালী করে তা encapsulate করতে আপনাকে একটি বিবরণকারী শ্রেণি ব্যবহার করতে হবে। এইভাবে, যদি বিবরণকারীটি কিছু ব্যয়বহুল ক্রিয়াকলাপ (উদাহরণস্বরূপ) ক্যাশে করতে ব্যবহৃত হয়, তবে এটি মানটিকে নিজের শ্রেণিতে নয় বরং সঞ্চয় করতে পারে।

বর্ণনাকারীদের সম্পর্কে একটি নিবন্ধ এখানে পাওয়া যাবে

সম্পাদনা করুন: হিসাবে jchl মন্তব্য নির্দিষ্ট, আপনি কেবল চেষ্টা করে Temperature.celsius, instanceহতে হবে None


6
মধ্যে পার্থক্য কি selfএবং instance?
লেলেমা প্রিজম

2
'উদাহরণ' কোনও শ্রেণীর উদাহরণ হতে পারে, স্ব একই শ্রেণীর উদাহরণ হতে পারে।
দ্য বিগেনার

3
@ লিম্পাপ্রিসম selfহ'ল বর্ণনাকারী উদাহরণ, instanceশ্রেণীর উদাহরণ (যদি তাত্ক্ষণিকভাবে হয়) বর্ণনাকারী ( instance.__class__ is owner) থাকে।
Tcll

Temperature.celsius0.0কোড অনুসারে মান দেয় celsius = Celsius()। বর্ণনাকারী সেলসিয়াস বলা হয়, সুতরাং এটির উদাহরণস্বরূপ 0.0তাপমাত্রা বর্গ বৈশিষ্ট্য, সেলসিয়াসের জন্য নির্ধারিত মান মান রয়েছে ।
অ্যাঞ্জেল সালাজার

109

আমার কেন বর্ণনাকারী শ্রেণি দরকার?

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

একটি বৈশিষ্ট্য কেবলমাত্র একটি পরিবর্তনীয় মান। একটি বর্ণনাকারী আপনাকে কোনও মান পড়তে বা সেট করতে (বা মুছতে) স্বেচ্ছাচারিত কোড কার্যকর করতে দেয়। সুতরাং আপনি এটি কোনও ডেটাবেজে কোনও ক্ষেত্রের কোনও বৈশিষ্ট্যের মানচিত্র তৈরি করতে কল্পনা করতে পারেন, উদাহরণস্বরূপ - এক ধরণের ওআরএম।

আর একটি ব্যবহার সম্ভবত একটি ব্যতিক্রম ছুঁড়ে দিয়ে একটি নতুন মান গ্রহণ করতে অস্বীকার করে __set__- কার্যকরভাবে কেবল "অ্যাট্রিবিউট "টিকে কেবল পঠনযোগ্য করে তোলে।

কি instanceএবং ownerএখানে? (ইন __get__) এই পরামিতিগুলির উদ্দেশ্য কী?

এটি বেশ সূক্ষ্ম (এবং যে কারণে আমি এখানে একটি নতুন উত্তর লিখছি - আমি একই প্রশ্নটি ভাবতে গিয়ে এই প্রশ্নটি পেয়েছি এবং বিদ্যমান উত্তরটি দুর্দান্ত খুঁজে পাইনি)।

একটি বর্ণনাকারী একটি শ্রেণীর উপর সংজ্ঞায়িত করা হয়, তবে সাধারণত একটি উদাহরণ থেকে ডেকে আনা হয়। যখন এটি একটি উদাহরণ থেকে উভয়কে ডাকা হয় instanceএবং ownerসেট করা হয় (এবং আপনি এটি ownerথেকে instanceবিন্দু অর্থহীন বলে মনে হয় তাই কাজ করতে পারেন )। কিন্তু যখন কোনও শ্রেণি থেকে কল করা হয়, কেবল ownerসেট করা হয় - সে কারণেই এটি সেখানে।

এটি কেবলমাত্র এর জন্য প্রয়োজন __get__কারণ এটি কেবলমাত্র ক্লাসে ডাকা যেতে পারে। আপনি যদি শ্রেণীর মান সেট করেন তবে আপনি নিজেই বিবরণী সেট করে। একইভাবে মুছে ফেলার জন্য। যে কারণে ownerসেখানে প্রয়োজন হয় না।

এই উদাহরণটি আমি কীভাবে কল / ব্যবহার করব?

ঠিক আছে, এখানে অনুরূপ ক্লাস ব্যবহার করে একটি দুর্দান্ত কৌশল:

class Celsius:

    def __get__(self, instance, owner):
        return 5 * (instance.fahrenheit - 32) / 9

    def __set__(self, instance, value):
        instance.fahrenheit = 32 + 9 * value / 5


class Temperature:

    celsius = Celsius()

    def __init__(self, initial_f):
        self.fahrenheit = initial_f


t = Temperature(212)
print(t.celsius)
t.celsius = 0
print(t.fahrenheit)

(আমি পাইথন 3 ব্যবহার করছি; পাইথন 2 এর জন্য আপনার অবশ্যই এই বিভাগগুলি নিশ্চিত হওয়া উচিত / 5.0এবং / 9.0)) এটি দেয়:

100.0
32.0

অজগরতে একই প্রভাব অর্জনের জন্য এখন আরও অনেক তর্কযোগ্য উপায় রয়েছে (উদাহরণস্বরূপ যদি সেলসিয়াস একটি সম্পত্তি ছিল যা একই মৌলিক প্রক্রিয়া তবে তাপমাত্রা বর্গের অভ্যন্তরে সমস্ত উত্স রাখে), তবে এটি দেখায় যে কী করা যায় ...


2
রূপান্তরগুলি ভুল: সেগুলি সি = 5 (এফ − 32) / 9, এফ = 32 + 9 সি / 5 হওয়া উচিত।
মুশিফিল

1
আপনার কাছে তাপমাত্রার একটি অবজেক্ট রয়েছে তা নিশ্চিত করুন। নিম্নলিখিত কাজগুলি জিনিস জড়িত। t1 = তাপমাত্রা (190) মুদ্রণ t1.celsius t1.celsius = 100 মুদ্রণ t1.fahrenheit এখন আপনি যখন t.celcius এবং t.fahrenheit পরীক্ষা করেন, সেগুলিও সংশোধিত হয়ে যায়। t.celcius 115 এবং t.fahrenheit 32. যা স্পষ্টতই ভুল। @ এরিক
ইশান ভট্ট

1
@ ইশানভট্ট: আমি মনে করি এটি উপরের মুসিফিল দ্বারা চিহ্নিত ত্রুটির কারণেই হয়েছে। এছাড়াও, এটি আমার উত্তর নয়
এরিক

68

আমি পাইথনের বর্ণনাকারী কী এবং সেগুলি কীভাবে কার্যকর হতে পারে তা বোঝার চেষ্টা করছি।

বর্ণনাকারী হ'ল নিম্নলিখিত বিশেষ পদ্ধতির সাথে শ্রেণীর বৈশিষ্ট্যগুলি (যেমন বৈশিষ্ট্য বা পদ্ধতিগুলি):

  • __get__ (অ-ডেটা বর্ণনাকারী পদ্ধতি, উদাহরণস্বরূপ কোনও পদ্ধতি / ফাংশনে)
  • __set__ (ডেটা বর্ণনাকারী পদ্ধতি, উদাহরণস্বরূপ কোনও সম্পত্তি উদাহরণে)
  • __delete__ (তথ্য বিবরণী পদ্ধতি)

এই বর্ণনাকারী অবজেক্টগুলিকে অন্যান্য অবজেক্ট শ্রেণির সংজ্ঞা হিসাবে অ্যাট্রিবিউট হিসাবে ব্যবহার করা যেতে পারে। ( __dict__এটি ক্লাস অবজেক্টের মধ্যে থাকে।

ডেস্ক্রিপ্টর অবজেক্টগুলি বিন্দুযুক্ত অনুসন্ধানের ফলাফলগুলি (যেমন foo.descriptor) একটি সাধারণ অভিব্যক্তি, একটি অ্যাসাইনমেন্ট এবং এমনকি একটি মুছে ফেলার ক্ষেত্রে প্রোগ্রামিয়ালি পরিচালনা করতে ব্যবহার করা যেতে পারে ।

কার্যাবলী / পদ্ধতি, আবদ্ধ পদ্ধতি, property, classmethod, এবং staticmethodসব ব্যবহারের নিয়ন্ত্রণের এই বিশেষ পদ্ধতি কিভাবে তারা ডটেড লুকআপ মাধ্যমে অ্যাক্সেস করা হয়।

একটি ডেটা বর্ণনাকারী , পছন্দproperty , বস্তুর একটি সহজ রাষ্ট্র উপর ভিত্তি করে গুণাবলীর অলস নিরীক্ষার জন্য অনুমতি দিতে পারেন, দৃষ্টান্ত চেয়ে যদি আপনি প্রতিটি সম্ভাব্য অ্যাট্রিবিউট precomputed কম মেমরি ব্যবহার করতে সক্ষম হবেন।

আরেকটি তথ্য বর্ণনাকারী, একটি member_descriptor, দ্বারা নির্মিত __slots__, একটি চপল ডেটা সংরক্ষণ করার বর্গ অনুমতি দিয়ে মেমরির সঞ্চয়ী অনুমতি tuple মত পরিবর্তে datastructure নমনীয় কিন্তু স্থান সাপেক্ষ __dict__

অ-ডেটা বর্ণনাকারী, সাধারণত উদাহরণস্বরূপ, শ্রেণি এবং স্থির পদ্ধতিগুলি তাদের অ-ডেটা বর্ণনাকারী পদ্ধতি থেকে তাদের অন্তর্নিহিত প্রথম যুক্তি (সাধারণত নাম clsএবং selfযথাক্রমে) পান __get__

পাইথনের বেশিরভাগ ব্যবহারকারীর কেবলমাত্র সাধারণ ব্যবহার শিখতে হবে, এবং আরও বিবরণীর প্রয়োগ আরও শিখতে বা বুঝতে হবে না।

গভীরতায়: বর্ণনাকারী কী?

বর্ণনাকারী হ'ল নীচের যে কোনও পদ্ধতি ( __get__,, __set__বা __delete__) যুক্ত একটি বস্তু যা বিন্দু-অনুসন্ধানের মাধ্যমে ব্যবহার করা হবে যেন এটি কোনও উদাহরণের বৈশিষ্ট্য। মালিক-অবজেক্টের জন্য, obj_instanceকোনও descriptorবস্তু সহ:

  • obj_instance.descriptor
    descriptor.__get__(self, obj_instance, owner_class)একটি প্রত্যাবর্তন আহ্বান value
    এইভাবে সমস্ত পদ্ধতি এবং getকোনও সম্পত্তি কাজ করে।

  • obj_instance.descriptor = valueপূজা
    descriptor.__set__(self, obj_instance, value)ফিরে None
    এই জন্যে কত setterএকটি সম্পত্তি উপর কাজ করে।

  • del obj_instance.descriptorপূজা
    descriptor.__delete__(self, obj_instance)ফিরে None
    এই জন্যে কত deleterএকটি সম্পত্তি উপর কাজ করে।

obj_instanceযার ক্লাসে বর্ণনাকারী অবজেক্টের উদাহরণ রয়েছে class বর্ণনাকারীরself উদাহরণ (সম্ভবত বর্গের জন্য কেবল একটিobj_instance )

কোড সহ এটি সংজ্ঞায়িত করতে, কোনও বস্তু হ'ল বর্ণনাকারী যদি এর বৈশিষ্ট্যগুলির সেটটি কোনও প্রয়োজনীয় বৈশিষ্ট্যের সাথে ছেদ করে:

def has_descriptor_attrs(obj):
    return set(['__get__', '__set__', '__delete__']).intersection(dir(obj))

def is_descriptor(obj):
    """obj can be instance of descriptor or the descriptor class"""
    return bool(has_descriptor_attrs(obj))

একটি ডেটা বর্ণনাকারী টি __set__এবং / অথবা __delete__
একটি অ-ডেটা-বর্ণনাকারীর নেই __set__এবং নেই __delete__

def has_data_descriptor_attrs(obj):
    return set(['__set__', '__delete__']) & set(dir(obj))

def is_data_descriptor(obj):
    return bool(has_data_descriptor_attrs(obj))

বিল্টিন বর্ণনাকারী অবজেক্টের উদাহরণ:

  • classmethod
  • staticmethod
  • property
  • সাধারণভাবে ফাংশন

অ-ডেটা বর্ণনাকারী

আমরা এটি দেখতে পারি classmethodএবং staticmethodঅ-ডেটা-বর্ণনাকারী:

>>> is_descriptor(classmethod), is_data_descriptor(classmethod)
(True, False)
>>> is_descriptor(staticmethod), is_data_descriptor(staticmethod)
(True, False)

উভয়েরই কেবল __get__পদ্ধতি রয়েছে:

>>> has_descriptor_attrs(classmethod), has_descriptor_attrs(staticmethod)
(set(['__get__']), set(['__get__']))

নোট করুন যে সমস্ত ফাংশন হ'ল নন-ডেটা-বর্ণনাকারী:

>>> def foo(): pass
... 
>>> is_descriptor(foo), is_data_descriptor(foo)
(True, False)

ডেটা বর্ণনাকারী, property

তবে, propertyএটি একটি ডেটা-বর্ণনাকারী:

>>> is_data_descriptor(property)
True
>>> has_descriptor_attrs(property)
set(['__set__', '__get__', '__delete__'])

ডটেড লুকআপ অর্ডার

এই গুরুত্বপূর্ণ প্রভেদ , তারা বিন্দু যুক্ত লুকআপ জন্য লুকআপ অর্ডার প্রভাবিত।

obj_instance.attribute
  1. প্রথমে উপরের অংশটি দেখতে হবে যে বৈশিষ্ট্যটি উদাহরণের শ্রেণিতে কোনও ডেটা-বর্ণনাকারী কিনা,
  2. যদি না হয়, এটা যদি অ্যাট্রিবিউট রয়েছে কিনা তা দেখে মনে হচ্ছে obj_instanceএর __dict__, তারপর
  3. অবশেষে এটি কোনও অ-ডেটা-বর্ণনাকারীর কাছে পড়ে।

এই অনুসন্ধানের আদেশের পরিণতি হ'ল নন-ডেটা-বর্ণনাকারীর মতো ফাংশন / পদ্ধতিগুলি উদাহরণগুলির দ্বারা ওভাররাইড করা যায়

পুনরুদ্ধার এবং পরবর্তী পদক্ষেপ

আমরা শিখেছি যে, বর্ণনাকারী সাথে বস্তু __get__, __set__বা __delete__। এই বর্ণনাকারী অবজেক্টগুলিকে অন্যান্য অবজেক্ট শ্রেণির সংজ্ঞা হিসাবে অ্যাট্রিবিউট হিসাবে ব্যবহার করা যেতে পারে। উদাহরণস্বরূপ আপনার কোডটি ব্যবহার করে এখন সেগুলি কীভাবে ব্যবহৃত হয় তা আমরা পর্যালোচনা করব।


প্রশ্ন থেকে কোড বিশ্লেষণ

এখানে আপনার কোড, আপনার প্রতিটি প্রশ্নের উত্তর এবং উত্তর:

class Celsius(object):
    def __init__(self, value=0.0):
        self.value = float(value)
    def __get__(self, instance, owner):
        return self.value
    def __set__(self, instance, value):
        self.value = float(value)

class Temperature(object):
    celsius = Celsius()
  1. আমার কেন বর্ণনাকারী শ্রেণি দরকার?

আপনার বর্ণনাকারী নিশ্চিত করে যে আপনার সর্বদা এই শ্রেণীর বৈশিষ্ট্যের জন্য একটি ভাসা রয়েছে Temperatureএবং আপনি delগুণটি মুছতে ব্যবহার করতে পারবেন না :

>>> t1 = Temperature()
>>> del t1.celsius
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: __delete__

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

class Temperature(object):
    celsius = 0.0

এটি আপনার উদাহরণের মতো ঠিক একই আচরণ পেয়েছে (নীচের প্রশ্নের 3 propertyটির প্রতিক্রিয়া দেখুন), তবে পাইথন বিল্টিন ( ) ব্যবহার করে এবং এটি আরও মূর্খ বলে বিবেচিত হবে:

class Temperature(object):
    _celsius = 0.0
    @property
    def celsius(self):
        return type(self)._celsius
    @celsius.setter
    def celsius(self, value):
        type(self)._celsius = float(value)
  1. উদাহরণ এবং মালিক এখানে কি? ( পেতে ) এই পরামিতিগুলির উদ্দেশ্য কী?

instanceএটি হ'ল মালিকের উদাহরণ যা বর্ণনাকারীকে কল করে। মালিক হ'ল শ্রেণি যেখানে বর্ণনাকারী অবজেক্টটি ডেটা পয়েন্টে অ্যাক্সেস পরিচালনা করতে ব্যবহৃত হয়। আরও বর্ণনামূলক পরিবর্তনশীল নামের জন্য এই উত্তরের প্রথম অনুচ্ছেদের পাশের বর্ণনাকারীদের সংজ্ঞায়িতকারী বিশেষ পদ্ধতির বর্ণনা দেখুন।

  1. এই উদাহরণটি আমি কীভাবে কল / ব্যবহার করব?

এখানে একটি বিক্ষোভ:

>>> t1 = Temperature()
>>> t1.celsius
0.0
>>> t1.celsius = 1
>>> 
>>> t1.celsius
1.0
>>> t2 = Temperature()
>>> t2.celsius
1.0

আপনি গুণটি মুছতে পারবেন না:

>>> del t2.celsius
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: __delete__

এবং আপনি কোনও ভেরিয়েবল বরাদ্দ করতে পারবেন না যা ফ্লোটে রূপান্তর করা যাবে না:

>>> t1.celsius = '0x02'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in __set__
ValueError: invalid literal for float(): 0x02

অন্যথায়, আপনার এখানে যা আছে তা সমস্ত দৃষ্টান্তের জন্য একটি বৈশ্বিক রাষ্ট্র, যা কোনও উদাহরণকে বরাদ্দ করে পরিচালিত হয়।

পাইথন প্রোগ্রামাররা যে ফলাফলটি প্রত্যাশিতভাবে সম্পন্ন করবে তা propertyহ'ল সাজসজ্জার ব্যবহার করা , যা একই বর্ণনাকারীকে হুডের নীচে ব্যবহার করে তবে মালিক শ্রেণীর প্রয়োগে আবার আচরণ করে (আবার উপরে বর্ণিত হিসাবে):

class Temperature(object):
    _celsius = 0.0
    @property
    def celsius(self):
        return type(self)._celsius
    @celsius.setter
    def celsius(self, value):
        type(self)._celsius = float(value)

যার কোডটির মূল অংশের ঠিক একই প্রত্যাশিত আচরণ রয়েছে:

>>> t1 = Temperature()
>>> t2 = Temperature()
>>> t1.celsius
0.0
>>> t1.celsius = 1.0
>>> t2.celsius
1.0
>>> del t1.celsius
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't delete attribute
>>> t1.celsius = '0x02'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 8, in celsius
ValueError: invalid literal for float(): 0x02

উপসংহার

আমরা বর্ণনাকারীদের সংজ্ঞা দেয় এমন বৈশিষ্ট্যগুলি, ডেটা- এবং অ-ডেটা-বিবরণকারীগুলির মধ্যে পার্থক্যগুলি, সেগুলি ব্যবহার করে এমন বিল্টিন অবজেক্ট এবং ব্যবহার সম্পর্কে নির্দিষ্ট প্রশ্নগুলি আবরণ করেছি।

আবার, আপনি কিভাবে প্রশ্নের উদাহরণ ব্যবহার করবেন? আমি আশা করি আপনি না। আমি আশা করি আপনি আমার প্রথম পরামর্শটি (একটি সাধারণ শ্রেণীর বৈশিষ্ট্য) দিয়ে শুরু করবেন এবং যদি আপনার প্রয়োজনীয়তা মনে হয় তবে দ্বিতীয় পরামর্শ (সম্পত্তি সজ্জাকার) এ এগিয়ে চলবেন।


1
ভাল, আমি এই উত্তরটি থেকে সবচেয়ে বেশি শিখেছি (অবশ্যই অন্যদের কাছ থেকেও শিখেছি)। এই বিবৃতি সম্পর্কে একটি প্রশ্ন "বেশিরভাগ অভিজ্ঞ পাইথন প্রোগ্রামাররা প্রত্যাশিত উপায়ে এই ফলাফলটি অর্জন করবে ..."। বিবৃতি দেওয়ার আগে এবং পরে আপনি যে টেম্পেরেশন ক্লাসটি সংজ্ঞায়িত করেছেন তা অভিন্ন are আপনি এখানে যা পেয়েছেন তা কি আমি মিস করেছি?
ইয়োলো ভো

1
@ ইয়োলোভো না, এটা ঠিক, আমি উপরের পুনরাবৃত্তিটি জোর দিয়ে কিছু প্যারেন্টেটিকাল ভার্বিয়েজ যুক্ত করেছি।
হারুন হলের

1
এটি গ্রহণযোগ্য উত্তর হওয়া উচিত
সেবাস্তিয়ান নীলসেন

1
এটি একটি আশ্চর্যজনক উত্তর। আমি এটি আরও কয়েকবার পড়তে হবে তবে আমার মনে হচ্ছে পাইথন সম্পর্কে আমার বোঝাপড়াটি কেবল কয়েকটি খাঁজ কাটিয়ে উঠেছে
লুকাস ইয়ং

20

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

এক্ষেত্রে অ্যাট্রিবিউট সন্ধানের সেরা চিত্রণ (পাইথন 3.x বা পাইথন ২.x-এ নতুন-স্টাইলের ক্লাসের জন্য) এর থেকে লুকিংয়ের পাইথন মেটাগ্লাস (আয়নেলের কোডেলোগ) বোঝা । চিত্রটি :"নন-কাস্টমাইজযোগ্য অ্যাট্রিবিউট লুকআপ" এর বিকল্প হিসাবে ব্যবহার করে ।

এই একটি বৈশিষ্ট্য এর লুকআপ প্রতিনিধিত্ব করে foobarএকটি অন instanceএর Class:

এখানে চিত্র বর্ণনা লিখুন

দুটি শর্ত এখানে গুরুত্বপূর্ণ:

  • যদি শ্রেণীর instanceগুণাবলীর নামের জন্য একটি এন্ট্রি থাকে এবং এটি থাকে __get__এবং থাকে __set__
  • তাহলে instanceহয়েছে কোন এটা আছে অ্যাট্রিবিউট নামের জন্য এন্ট্রি কিন্তু বর্গ এক হয়েছে এবং __get__

এটি এখানে বর্ণনাকারী আসে:

  • ডেটা বর্ণনাকারী যা উভয় __get__এবং __set__
  • কেবলমাত্র অ-ডেটা বর্ণনাকারী__get__

উভয় ক্ষেত্রে প্রত্যাশিত মানটিকে __get__প্রথম আর্গুমেন্ট হিসাবে এবং ক্লাসটিকে দ্বিতীয় আর্গুমেন্ট হিসাবে ডাকা হয়।

শ্রেণিবদ্ধ বৈশিষ্ট্য অনুসন্ধানের জন্য চেহারা আরও জটিল (উদাহরণস্বরূপ শ্রেণি বৈশিষ্ট্য অনুসন্ধান) (উপরে বর্ণিত ব্লগে) )।

আসুন আপনার নির্দিষ্ট প্রশ্নে সরানো যাক:

আমার কেন বর্ণনাকারী শ্রেণি দরকার?

বেশিরভাগ ক্ষেত্রে আপনার বর্ণনামূলক ক্লাস লেখার দরকার নেই! তবে আপনি সম্ভবত খুব নিয়মিত শেষ ব্যবহারকারী। উদাহরণস্বরূপ ফাংশন। ফাংশনগুলি হ'ল বর্ণনাকারী, এভাবেই selfপ্রথম আর্গুমেন্ট হিসাবে স্পষ্টভাবে পাস দিয়ে ফাংশনগুলি পদ্ধতি হিসাবে ব্যবহার করা যেতে পারে ।

def test_function(self):
    return self

class TestClass(object):
    def test_method(self):
        ...

আপনি যদি test_methodএকটি নজরে সন্ধান করেন তবে আপনি একটি "আবদ্ধ পদ্ধতি" ফিরে পাবেন:

>>> instance = TestClass()
>>> instance.test_method
<bound method TestClass.test_method of <__main__.TestClass object at ...>>

একইভাবে আপনি __get__নিজে নিজে কোনও পদ্ধতিতে তার পদ্ধতিটি আহ্বান করে বাঁধতে পারেন (সত্যিকার অর্থে প্রস্তাবিত নয়, কেবল উদাহরণস্বরূপ উদ্দেশ্যে):

>>> test_function.__get__(instance, TestClass)
<bound method test_function of <__main__.TestClass object at ...>>

এমনকি আপনি এটিকে "স্ব-বাধা পদ্ধতি" বলতে পারেন:

>>> test_function.__get__(instance, TestClass)()
<__main__.TestClass at ...>

মনে রাখবেন যে আমি কোনও যুক্তি সরবরাহ করি নি এবং ফাংশনটি আমার আবদ্ধ হওয়া দৃষ্টান্তটি ফিরিয়ে দিয়েছে!

ফাংশনগুলি হ'ল ডেটা বর্ণনাকারী !

ডেটা-বর্ণনাকারীর অন্তর্নির্মিত কয়েকটি উদাহরণ হবে property। অবহেলা getter, setterএবং বর্ণনাকারী আছে (থেকে বর্ণনাকারী এ HowTo গাইড "বিশিষ্টতাসমূহ" ):deleterproperty

class Property(object):
    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
        self.fget = fget
        self.fset = fset
        self.fdel = fdel
        if doc is None and fget is not None:
            doc = fget.__doc__
        self.__doc__ = doc

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        if self.fget is None:
            raise AttributeError("unreadable attribute")
        return self.fget(obj)

    def __set__(self, obj, value):
        if self.fset is None:
            raise AttributeError("can't set attribute")
        self.fset(obj, value)

    def __delete__(self, obj):
        if self.fdel is None:
            raise AttributeError("can't delete attribute")
        self.fdel(obj)

যেহেতু এটা একটি ডাটা বর্ণনাকারী এটা প্রার্থনা হচ্ছে যখনই তোমাদের "নাম" সন্ধান propertyএবং এটি কেবল ফাংশন দিয়ে সাজানো প্রতিনিধিদের @property, @name.setterএবং@name.deleter (যদি থাকে)।

স্ট্যান্ডার্ড লাইব্রেরিতে আরও কয়েকটি বর্ণনাকারী রয়েছে, উদাহরণস্বরূপ staticmethod,classmethod

বর্ণনাকারীদের বিন্দুটি সহজ (যদিও আপনার খুব কমই এগুলির প্রয়োজন হয়): বৈশিষ্ট্য অ্যাক্সেসের জন্য বিমূর্ত সাধারণ কোড। propertyউদাহরণ পরিবর্তনশীল অ্যাক্সেসের functionজন্য বিমূর্ততা, পদ্ধতিগুলির জন্য একটি বিমূর্ততা সরবরাহ করে, staticmethodএমন পদ্ধতির জন্য একটি বিমূর্ততা সরবরাহ করে যা উদাহরণ অ্যাক্সেসের প্রয়োজন হয় না এবংclassmethod জন্য একটি বিমূর্ততা সরবরাহ করে অ্যাক্সেসের পরিবর্তে ক্লাস অ্যাক্সেসের প্রয়োজন এমন পদ্ধতির জন্য একটি বিমূর্ততা সরবরাহ করে (এটি কিছুটা সরলীকৃত)।

আর একটি উদাহরণ বর্গ সম্পত্তি হবে

একটি মজাদার উদাহরণ ( __set_name__পাইথন ৩.6 থেকে ব্যবহার করে ) এমন একটি সম্পত্তিও হতে পারে যা কেবলমাত্র একটি নির্দিষ্ট ধরণের অনুমতি দেয়:

class TypedProperty(object):
    __slots__ = ('_name', '_type')
    def __init__(self, typ):
        self._type = typ

    def __get__(self, instance, klass=None):
        if instance is None:
            return self
        return instance.__dict__[self._name]

    def __set__(self, instance, value):
        if not isinstance(value, self._type):
            raise TypeError(f"Expected class {self._type}, got {type(value)}")
        instance.__dict__[self._name] = value

    def __delete__(self, instance):
        del instance.__dict__[self._name]

    def __set_name__(self, klass, name):
        self._name = name

তারপরে আপনি কোনও ক্লাসে বর্ণনাকারী ব্যবহার করতে পারেন:

class Test(object):
    int_prop = TypedProperty(int)

এবং এটির সাথে কিছুটা খেলুন:

>>> t = Test()
>>> t.int_prop = 10
>>> t.int_prop
10

>>> t.int_prop = 20.0
TypeError: Expected class <class 'int'>, got <class 'float'>

বা একটি "অলস সম্পত্তি":

class LazyProperty(object):
    __slots__ = ('_fget', '_name')
    def __init__(self, fget):
        self._fget = fget

    def __get__(self, instance, klass=None):
        if instance is None:
            return self
        try:
            return instance.__dict__[self._name]
        except KeyError:
            value = self._fget(instance)
            instance.__dict__[self._name] = value
            return value

    def __set_name__(self, klass, name):
        self._name = name

class Test(object):
    @LazyProperty
    def lazy(self):
        print('calculating')
        return 10

>>> t = Test()
>>> t.lazy
calculating
10
>>> t.lazy
10

এগুলি এমন ক্ষেত্রে যেখানে যুক্তিটিকে একটি সাধারণ বর্ণনাকারীতে স্থানান্তরিত করা অর্থবহ হতে পারে, তবে যে কোনও একটি তাদের অন্য উপায়ে সমাধান করতে পারে (তবে কিছু কোড পুনরাবৃত্তি করে)।

কি instanceএবং ownerএখানে? (ইন __get__) এই পরামিতিগুলির উদ্দেশ্য কী?

এটি নির্ভর করে আপনি কীভাবে বৈশিষ্ট্যটি দেখছেন on আপনি যদি কোনও উদাহরণটিতে গুণটি সন্ধান করেন তবে:

  • দ্বিতীয় যুক্তি হ'ল উদাহরণটি যার উপরে আপনি গুনটি দেখেন
  • তৃতীয় যুক্তি উদাহরণের শ্রেণি

আপনি যদি ক্লাসে অ্যাট্রিবিউটটি সন্ধান করেন (ধরে নিবেন শ্রেণীর উপর বর্ণনাকারী সংজ্ঞা দেওয়া হয়েছে):

  • দ্বিতীয় যুক্তি হল None
  • তৃতীয় আর্গুমেন্টটি এমন শ্রেণি যেখানে আপনি গুণটি সন্ধান করেন

তাই মূলত তৃতীয় যুক্তি যদি আপনি শ্রেণী-স্তরের লুক-আপ (কারণ কি আচরণ কাস্টমাইজ করতে চান প্রয়োজনীয় instanceহয় None)।

এই উদাহরণটি আমি কীভাবে কল / ব্যবহার করব?

আপনার উদাহরণটি মূলত এমন একটি সম্পত্তি যা কেবলমাত্র মানগুলিকে রূপান্তর করতে পারে floatএবং যা শ্রেণীর সমস্ত উদাহরণের মধ্যে ভাগ করা যায় (এবং শ্রেণিতে - যদিও কেউ কেবল ক্লাসে "পঠন" অ্যাক্সেস ব্যবহার করতে পারে অন্যথায় আপনি বিবরণীর উদাহরণটি প্রতিস্থাপন করতে পারবেন) ):

>>> t1 = Temperature()
>>> t2 = Temperature()

>>> t1.celsius = 20   # setting it on one instance
>>> t2.celsius        # looking it up on another instance
20.0

>>> Temperature.celsius  # looking it up on the class
20.0

এজন্য বর্ণনাকারীরা সাধারণত দ্বিতীয়টি যুক্তি ( instance) ব্যবহার করে যাতে এটিকে ভাগ না করে তা সঞ্চয় করে। তবে কিছু ক্ষেত্রে উদাহরণগুলির মধ্যে একটি মান ভাগ করা পছন্দসই হতে পারে (যদিও আমি এই মুহুর্তে কোনও দৃশ্যের কথা ভাবতে পারি না)। তবে এটি কোনও তাপমাত্রার শ্রেণিতে সেলসিয়াস সম্পত্তির জন্য ব্যবহারিকভাবে কোনও ধারণা দেয় না ... কেবল বিশুদ্ধভাবে একাডেমিক অনুশীলন হিসাবে।


নিশ্চিত না যে গ্রাফিকের স্বচ্ছ পটভূমিটি সত্যিই অন্ধকার মোডে ভুগছে কিনা তা স্ট্যাকওভারফ্লোতে বাগ হিসাবে রিপোর্ট করা উচিত।
Tshirtman

@ শার্টম্যান আমি মনে করি এটি ইমেজটিতেই একটি সমস্যা। এটি সম্পূর্ণ স্বচ্ছ নয় ... আমি এটি ব্লগ পোস্ট থেকে নিয়েছি এবং সঠিক স্বচ্ছ পটভূমি দিয়ে কীভাবে এটি পুনরায় তৈরি করতে হয় তা আমি জানি না। এটি খুব খারাপ এটি অন্ধকার পটভূমির সাথে এতটাই অদ্ভুত দেখাচ্ছে :(
এমেসিফার্ট

9

আমার কেন বর্ণনাকারী শ্রেণি দরকার?

বুকিয়ানো রামালহোর ফ্লুয়েন্ট পাইথন দ্বারা অনুপ্রাণিত

ইমেজিং আপনার মত একটি ক্লাস আছে

class LineItem:
     price = 10.9
     weight = 2.1
     def __init__(self, name, price, weight):
          self.name = name
          self.price = price
          self.weight = weight

item = LineItem("apple", 2.9, 2.1)
item.price = -0.9  # it's price is negative, you need to refund to your customer even you delivered the apple :(
item.weight = -0.8 # negative weight, it doesn't make sense

তাদের নেতিবাচক সংখ্যা নির্ধারণ করা এড়াতে আমাদের ওজন ও দামকে বৈধতা দেওয়া উচিত, আমরা যদি প্রসিকি হিসাবে বর্ণনাকারী হিসাবে এটি ব্যবহার করি তবে আমরা কম কোড লিখতে পারি

class Quantity(object):
    __index = 0

    def __init__(self):
        self.__index = self.__class__.__index
        self._storage_name = "quantity#{}".format(self.__index)
        self.__class__.__index += 1

    def __set__(self, instance, value):
        if value > 0:
            setattr(instance, self._storage_name, value)
        else:
           raise ValueError('value should >0')

   def __get__(self, instance, owner):
        return getattr(instance, self._storage_name)

তারপরে ক্লাস লাইন আইটেমটি এভাবে সংজ্ঞায়িত করুন:

class LineItem(object):
     weight = Quantity()
     price = Quantity()

     def __init__(self, name, weight, price):
         self.name = name
         self.weight = weight
         self.price = price

এবং আমরা আরও সাধারণ বৈধতা দেওয়ার জন্য কোয়ানটিটি ক্লাসটি বাড়িয়ে দিতে পারি


1
আকর্ষণীয় ব্যবহারের ক্ষেত্রে, যেমন এটি দেখায় যে কীভাবে ব্যবহারকারীদের একাধিক উদাহরণের সাথে ইন্টারঅ্যাক্ট করতে বর্ণনাকারী ব্যবহার করতে পারেন। আমি প্রথমে গুরুত্বপূর্ণ বিষয়টি বুঝতে পারি নি: শ্রেণীর নেমস্পেসে একটি বর্ণনাকারীর সাহায্যে একটি গুণাবলী তৈরি করা আবশ্যক (উদাহরণস্বরূপ weight = Quantity(), তবে মানগুলি অবশ্যই নাম নামের ক্ষেত্রে সেট করা উচিত self(উদাহরণস্বরূপ self.weight = 4)), অন্যথায় বৈশিষ্ট্যটি নতুন মানটিতে প্রত্যাবর্তন করবে এবং বর্ণনাকারী ফেলে দেওয়া হবে N চমৎকার!
মিনিট

আমি একটি জিনিস বুঝতে সক্ষম হয় না। আপনি weight = Quantity()ক্লাস ভেরিয়েবল এবং এর হিসাবে সংজ্ঞা দিচ্ছেন __get__এবং __set__ইনস্ট্যান্স ভেরিয়েবলের উপর কাজ করছেন। কিভাবে?
টেকনোক্র্যাট

0

অ্যান্ড্রু কুকের উত্তর থেকে কোডটি (প্রস্তাবিত ছোট ছোট পরিবর্তন সহ) চেষ্টা করেছি। (আমি অজগর ২. 2. চালাচ্ছি)।

কোড:

#!/usr/bin/env python
class Celsius:
    def __get__(self, instance, owner): return 9 * (instance.fahrenheit + 32) / 5.0
    def __set__(self, instance, value): instance.fahrenheit = 32 + 5 * value / 9.0

class Temperature:
    def __init__(self, initial_f): self.fahrenheit = initial_f
    celsius = Celsius()

if __name__ == "__main__":

    t = Temperature(212)
    print(t.celsius)
    t.celsius = 0
    print(t.fahrenheit)

ফলাফল:

C:\Users\gkuhn\Desktop>python test2.py
<__main__.Celsius instance at 0x02E95A80>
212

পাইথন 3 পূর্বে সঙ্গে, নিশ্চিত করুন যে আপনি বস্তুর বর্ণনাকারী কাজ সঠিকভাবে করতে হবে যেমন থেকে উপশ্রেণী করতে পেতে যাদু পুরানো শৈলী শ্রেণীর জন্য কাজ করে না।


1
বর্ণনাকারীরা কেবলমাত্র নতুন স্টাইলের ক্লাস নিয়ে কাজ করেন। পাইথন ২.x এর অর্থ হল "অবজেক্ট" থেকে আপনার ক্লাস নেওয়া, যা পাইথন ৩ এ ডিফল্ট
আইভো ভ্যান ডের উইজক

0

আপনি https://docs.python.org/3/howto/descriptor.html#properties দেখতে পাবেন

class Property(object):
    "Emulate PyProperty_Type() in Objects/descrobject.c"

    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
        self.fget = fget
        self.fset = fset
        self.fdel = fdel
        if doc is None and fget is not None:
            doc = fget.__doc__
        self.__doc__ = doc

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        if self.fget is None:
            raise AttributeError("unreadable attribute")
        return self.fget(obj)

    def __set__(self, obj, value):
        if self.fset is None:
            raise AttributeError("can't set attribute")
        self.fset(obj, value)

    def __delete__(self, obj):
        if self.fdel is None:
            raise AttributeError("can't delete attribute")
        self.fdel(obj)

    def getter(self, fget):
        return type(self)(fget, self.fset, self.fdel, self.__doc__)

    def setter(self, fset):
        return type(self)(self.fget, fset, self.fdel, self.__doc__)

    def deleter(self, fdel):
        return type(self)(self.fget, self.fset, fdel, self.__doc__)

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