সাবক্লাসিং: প্রচলিত বৈশিষ্ট্যযুক্ত কোনও সম্পত্তি কী ওভাররাইড করা সম্ভব?


14

আসুন ধরে নেওয়া যাক আমরা এমন একটি ক্লাসের পরিবার তৈরি করতে চাই যা বিভিন্ন বাস্তবায়ন বা একটি অতিরিক্ত ধারণাটির বিশেষীকরণ special আসুন ধরে নেওয়া যাক কিছু উত্পন্ন বৈশিষ্ট্যগুলির জন্য প্লাজেবল ডিফল্ট বাস্তবায়ন রয়েছে। আমরা এটিকে বেস ক্লাসে রাখতে চাই

class Math_Set_Base:
    @property
    def size(self):
        return len(self.elements)

সুতরাং একটি সাবক্লাস স্বয়ংক্রিয়ভাবে এর পরিবর্তে নির্বোধ উদাহরণে এর উপাদানগুলি গণনা করতে সক্ষম হবে

class Concrete_Math_Set(Math_Set_Base):
    def __init__(self,*elements):
        self.elements = elements

Concrete_Math_Set(1,2,3).size
# 3

কিন্তু যদি একটি সাবক্লাস এই ডিফল্টটি ব্যবহার করতে না চায়? এটা কাজ করে না:

import math

class Square_Integers_Below(Math_Set_Base):
    def __init__(self,cap):
        self.size = int(math.sqrt(cap))

Square_Integers_Below(7)
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
#   File "<stdin>", line 3, in __init__
# AttributeError: can't set attribute

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

এটা করা যায়? না হলে পরবর্তী সেরা সমাধান কি?

উত্তর:


6

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

import math

class non_data_property:
    def __init__(self, fget):
        self.__doc__ = fget.__doc__
        self.fget = fget

    def __get__(self, obj, cls):
        if obj is None:
            return self
        return self.fget(obj)

class Math_Set_Base:
    @non_data_property
    def size(self, *elements):
        return len(self.elements)

class Concrete_Math_Set(Math_Set_Base):
    def __init__(self, *elements):
        self.elements = elements


class Square_Integers_Below(Math_Set_Base):
    def __init__(self, cap):
        self.size = int(math.sqrt(cap))

print(Concrete_Math_Set(1, 2, 3).size) # 3
print(Square_Integers_Below(1).size) # 1
print(Square_Integers_Below(4).size) # 2
print(Square_Integers_Below(9).size) # 3

তবে এটি ধরে নেয় যে এই পরিবর্তনগুলি করার জন্য আপনার কাছে বেস ক্লাসে অ্যাক্সেস রয়েছে।


এটি নিখুঁত কাছাকাছি। সুতরাং এর অর্থ একটি পপার্টি এমনকি যদি আপনি কেবল গ্রাহককে সংজ্ঞায়িতভাবে একটি সেটার যুক্ত করেন যা অ্যাসাইনমেন্টকে বাধা দেওয়া ছাড়া কিছুই করে না? মজাদার. আমি সম্ভবত এই উত্তর গ্রহণ করব।
পল প্যানজার

হ্যাঁ, ডক্স অনুসারে, কোনও সম্পত্তি সর্বদা তিনটি বর্ণনামূলক পদ্ধতি ( __get__(), __set__()এবং __delete__()) সংজ্ঞায়িত করে এবং AttributeErrorআপনি যদি তাদের জন্য কোনও কার্য সরবরাহ না করেন তবে তারা একটি উত্থাপন করে। সম্পত্তি বাস্তবায়নের সমকক্ষ পাইথন দেখুন ।
আর্কিলিস

যতক্ষণ না আপনি সম্পত্তি setterবা তার কোনও যত্ন নিচ্ছেন না __set__, এটি কাজ করবে। তবে আমি আপনাকে সাবধান করে দেব যে এর অর্থ হ'ল আপনি সহজে সম্পত্তি কেবল শ্রেণিবদ্ধ স্তর ( self.size = ...) এ নয় উদাহরণস্বরূপ স্তরেও (উদাহরণস্বরূপ Concrete_Math_Set(1, 2, 3).size = 10সমান বৈধ হতে পারবেন ) ওভাররাইট করতে পারেন । চিন্তার জন্য খাদ্য :)
r.ook

আর Square_Integers_Below(9).size = 1যেমন একটি সহজ অ্যাট্রিবিউট এছাড়াও বৈধ। এই বিশেষ "আকার" ব্যবহারের ক্ষেত্রে এটি আক্কর মনে হতে পারে (পরিবর্তে কোনও সম্পত্তি ব্যবহার করুন) তবে সাধারণ ক্ষেত্রে "সাবক্লাসে সহজেই একটি গণনামূলক প্রপাকে ওভাররাইড করুন", কিছু ক্ষেত্রে এটি ভাল হতে পারে। আপনি এর সাথে অ্যাট্রিবিউট অ্যাক্সেসও নিয়ন্ত্রণ করতে পারেন __setattr__()তবে এটি অপ্রতিরোধ্য হতে পারে।
আরকেলেস

1
এগুলির জন্য বৈধ ব্যবহারের মামলা থাকতে পারে বলে আমি দ্বিমত পোষণ করছি না, আমি কেবলমাত্র ক্যাভ্যাটটি উল্লেখ করতে চেয়েছিলাম কারণ এটি ভবিষ্যতে ডিবাগিংয়ের প্রচেষ্টাকে জটিল করে তুলতে পারে। যতক্ষণ না আপনি এবং ওপি উভয়ই এর সম্পর্কে জেনে থাকেন ততক্ষণে সবকিছু ঠিক আছে।
r.ook

9

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

আপনি শেষ পর্যন্ত এই উত্তরটি আপনার প্রকৃত সমস্যার পক্ষে সহায়ক নাও হতে পারেন। আসলে, আমার উপসংহারটি হ'ল - আমি এটি কিছু করব না would এই বলে যে, এই উপসংহারের পটভূমি আপনাকে কিছুটা বিনোদন দিতে পারে, যেহেতু আপনি আরও বিশদ অনুসন্ধান করছেন।


কিছু ভুল ধারণা সম্বোধন

প্রথম উত্তরটি বেশিরভাগ ক্ষেত্রে সঠিক হলেও সর্বদা ক্ষেত্রে হয় না । উদাহরণস্বরূপ, এই শ্রেণিটি বিবেচনা করুন:

class Foo:
    def __init__(self):
        self.name = 'Foo!'
        @property
        def inst_prop():
            return f'Retrieving {self.name}'
        self.inst_prop = inst_prop

inst_propএকটি হওয়ার সময় property, অকাট্যভাবে একটি উদাহরণ বৈশিষ্ট্য:

>>> Foo.inst_prop
Traceback (most recent call last):
  File "<pyshell#60>", line 1, in <module>
    Foo.inst_prop
AttributeError: type object 'Foo' has no attribute 'inst_prop'
>>> Foo().inst_prop
<property object at 0x032B93F0>
>>> Foo().inst_prop.fget()
'Retrieving Foo!'

এটি আপনার নির্ভর করে যেখানেproperty প্রথম স্থানে সংজ্ঞা দেওয়া হয়েছে all যদি আপনার @propertyশ্রেণি "স্কোপ" (বা সত্যই, এর namespace) মধ্যে সংজ্ঞায়িত করা হয় তবে এটি একটি শ্রেণি বৈশিষ্ট্য হিসাবে পরিণত হয়। আমার উদাহরণস্বরূপ, বর্গ নিজেই inst_propইনস্ট্যান্ট না হওয়া পর্যন্ত কোনও সম্পর্কে সচেতন নয় । অবশ্যই, এটি এখানে সম্পত্তি হিসাবে মোটেই কার্যকর নয়।


তবে প্রথমে আসুন উত্তরাধিকারের সমাধান সম্পর্কে আপনার মন্তব্যে ...

সুতরাং ঠিক কিভাবে এই সমস্যা মধ্যে উত্তরাধিকার ফ্যাক্টর? এই নিম্নলিখিত নিবন্ধটি বিষয়টিতে এবং পদ্ধতি সমাধানের আদেশকে কিছুটা ডাইভ করে কিছুটা সম্পর্কিত, যদিও এটি গভীরতার পরিবর্তে উত্তরাধিকারের প্রশস্ততা নিয়ে আলোচনা করে।

আমাদের সন্ধানের সাথে সম্মিলিত, নীচে সেটআপ দেওয়া হয়েছে:

@property
def some_prop(self):
    return "Family property"

class Grandparent:
    culture = some_prop
    world_view = some_prop

class Parent(Grandparent):
    world_view = "Parent's new world_view"

class Child(Parent):
    def __init__(self):
        try:
            self.world_view = "Child's new world_view"
            self.culture = "Child's new culture"
        except AttributeError as exc:
            print(exc)
            self.__dict__['culture'] = "Child's desired new culture"

এই লাইনগুলি কার্যকর করা হলে কী হবে তা কল্পনা করুন:

print("Instantiating Child class...")
c = Child()
print(f'c.__dict__ is: {c.__dict__}')
print(f'Child.__dict__ is: {Child.__dict__}')
print(f'c.world_view is: {c.world_view}')
print(f'Child.world_view is: {Child.world_view}')
print(f'c.culture is: {c.culture}')
print(f'Child.culture is: {Child.culture}')

ফলাফলটি এইভাবে:

Instantiating Child class...
can't set attribute
c.__dict__ is: {'world_view': "Child's new world_view", 'culture': "Child's desired new culture"}
Child.__dict__ is: {'__module__': '__main__', '__init__': <function Child.__init__ at 0x0068ECD8>, '__doc__': None}
c.world_view is: Child's new world_view
Child.world_view is: Parent's new world_view
c.culture is: Family property
Child.culture is: <property object at 0x00694C00>

কিভাবে লক্ষ করুন:

  1. self.world_viewপ্রয়োগ করতে সক্ষম হয়েছিল, self.cultureব্যর্থ হওয়ার পরেও
  2. cultureChild.__dict__( mappingproxyক্লাসের মধ্যে উপস্থিত নেই , উদাহরণের সাথে বিভ্রান্ত হওয়ার দরকার নেই __dict__)
  3. যদিও এটি cultureবিদ্যমান c.__dict__, এটি উল্লেখ করা হয়নি।

আপনি কেন অনুমান করতে সক্ষম হবেন - শ্রেণি world_viewদ্বারা Parentএকটি অ-সম্পত্তি Childহিসাবে ওভাররাইট করা হয়েছিল , তাই এটিও ওভাররাইট করতে সক্ষম হয়েছিল। এদিকে, যেহেতু cultureউত্তরাধিকার হিসেবে প্রাপ্ত করা হয়, এটি শুধুমাত্র মধ্যে বিদ্যমান mappingproxyএরGrandparent :

Grandparent.__dict__ is: {
    '__module__': '__main__', 
    'culture': <property object at 0x00694C00>, 
    'world_view': <property object at 0x00694C00>, 
    ...
}

আসলে আপনি যদি অপসারণের চেষ্টা করেন Parent.culture:

>>> del Parent.culture
Traceback (most recent call last):
  File "<pyshell#67>", line 1, in <module>
    del Parent.culture
AttributeError: culture

আপনি খেয়াল করবেন এটি এর পক্ষেও নেই Parent। কারণ অবজেক্টটি সরাসরি উল্লেখ করছে Grandparent.culture


সুতরাং, রেজোলিউশন অর্ডার সম্পর্কে কী?

সুতরাং আমরা প্রকৃত রেজোলিউশন অর্ডারটি পর্যবেক্ষণ করতে আগ্রহী, এর Parent.world_viewপরিবর্তে সরানোর চেষ্টা করি :

del Parent.world_view
print(f'c.world_view is: {c.world_view}')
print(f'Child.world_view is: {Child.world_view}')

ভাবছি ফলাফল কী?

c.world_view is: Family property
Child.world_view is: <property object at 0x00694C00>

এটি পিতামাতার কাছে ফিরে আসে world_view property, যদিও আমরা আগে সাফল্য সাফল্যের সাথে পরিচালনা করেছিলাম self.world_view! তবে কী যদি আমরা world_viewজবাবের সাথে ক্লাস পর্যায়ে অন্যান্য উত্তরগুলির মতো পরিবর্তন করি ? যদি আমরা এটি মুছে ফেলি? যদি আমরা বর্তমান শ্রেণীর বৈশিষ্ট্যটিকে সম্পত্তি হিসাবে নির্ধারিত করি তবে কী হবে?

Child.world_view = "Child's independent world_view"
print(f'c.world_view is: {c.world_view}')
print(f'Child.world_view is: {Child.world_view}')

del c.world_view
print(f'c.world_view is: {c.world_view}')
print(f'Child.world_view is: {Child.world_view}')

Child.world_view = property(lambda self: "Child's own property")
print(f'c.world_view is: {c.world_view}')
print(f'Child.world_view is: {Child.world_view}')

ফলাফল হলো:

# Creating Child's own world view
c.world_view is: Child's new world_view
Child.world_view is: Child's independent world_view

# Deleting Child instance's world view
c.world_view is: Child's independent world_view
Child.world_view is: Child's independent world_view

# Changing Child's world view to the property
c.world_view is: Child's own property
Child.world_view is: <property object at 0x020071B0>

এটি আকর্ষণীয় কারণ কারণ c.world_viewএটির বৈশিষ্ট্যটিতে পুনরুদ্ধার করা হয়েছে, যখন Child.world_viewআমরা নির্ধারিত এক। উদাহরণের বৈশিষ্ট্যটি সরানোর পরে এটি শ্রেণীর বৈশিষ্ট্যে ফিরে আসে। এবং Child.world_viewসম্পত্তিটিতে পুনর্নির্দিষ্ট করার পরে , আমরা তাত্ক্ষণিকভাবে উদাহরণ বৈশিষ্ট্যের অ্যাক্সেসটি হারাব।

অতএব, আমরা নিম্নলিখিত রেজোলিউশন অর্ডারটি মেনে নিতে পারি :

  1. একটি বর্গ অ্যাট্রিবিউট উপস্থিত থাকলে এবং এটি একটি হল propertyমাধ্যমে এর মান পুনরুদ্ধার getterবা fget(পরে এই বিষয়ে আরও)। বর্তমান শ্রেণি প্রথম থেকে বেস শ্রেণিতে সর্বশেষ।
  2. অন্যথায়, যদি কোনও উদাহরণ অ্যাট্রিবিউট উপস্থিত থাকে, উদাহরণ বৈশিষ্ট্যটির মানটি পুনরুদ্ধার করুন।
  3. অন্যথায়, শ্রেণিবদ্ধ propertyগুণাবলী পুনরুদ্ধার করুন । বর্তমান শ্রেণি প্রথম থেকে বেস শ্রেণিতে সর্বশেষ।

সেক্ষেত্রে মূলটি মুছে ফেলা যাক property:

del Grandparent.culture
print(f'c.culture is: {c.culture}')
print(f'Child.culture is: {Child.culture}')

যা দেয়:

c.culture is: Child's desired new culture
Traceback (most recent call last):
  File "<pyshell#74>", line 1, in <module>
    print(f'Child.culture is: {Child.culture}')
AttributeError: type object 'Child' has no attribute 'culture'

Ta-Dah! শক্তিশালী সন্নিবেশের উপর Childএখন তাদের নিজস্ব cultureভিত্তি রয়েছে c.__dict__Child.cultureঅবশ্যই উপস্থিত নেই, যেহেতু এটি কখনই শ্রেণীবদ্ধ Parentবা সংজ্ঞা হিসাবে সংজ্ঞায়িত হয়নি Childএবং Grandparentএর অপসারণ করা হয়েছিল।


এটাই কি আমার সমস্যার মূল কারণ?

আসলে, না । আপনি যখন ত্রুটিটি পেয়ে যাচ্ছেন, যা আমরা নির্ধারিত হওয়ার পরেও পর্যবেক্ষণ করছি self.culture, সম্পূর্ণ ভিন্ন । কিন্তু উত্তরাধিকার আদেশ উত্তরের প্রেক্ষাপট সেট করে - যা propertyনিজেই।

পূর্বে উল্লিখিত getterপদ্ধতিটি ছাড়াও এর propertyআস্তিনে কয়েকটি ঝরঝরে কৌশলও রয়েছে। এই ক্ষেত্রে সর্বাধিক প্রাসঙ্গিক হ'ল setter, বা fsetপদ্ধতি, যা self.culture = ...লাইন দ্বারা ট্রিগার করা হয় । যেহেতু আপনার propertyকোনও কাজ setterবা fgetফাংশন বাস্তবায়ন হয়নি , তাই অজগর কী করণীয় তা জানে না এবং AttributeErrorপরিবর্তে (অর্থাত্ can't set attribute) ফেলে দেয় ।

তবে আপনি যদি একটি setterপদ্ধতি প্রয়োগ করেন :

@property
def some_prop(self):
    return "Family property"

@some_prop.setter
def some_prop(self, val):
    print(f"property setter is called!")
    # do something else...

Childক্লাসটি ইনস্ট্যান্ট করার সময় আপনি পাবেন:

Instantiating Child class...
property setter is called!

এটির পরিবর্তে AttributeErrorআপনি এখন some_prop.setterপদ্ধতিটি কল করছেন । যা আপনাকে আপনার অবজেক্টের উপর আরও নিয়ন্ত্রণ দেয় ... আমাদের পূর্ববর্তী অনুসন্ধানগুলির সাথে, আমরা জানি যে এটি সম্পত্তি পৌঁছানোর আগে আমাদের একটি শ্রেণির বৈশিষ্ট্য ওভাররাইট করা দরকার । এটি ট্রিগার হিসাবে বেস শ্রেণীর মধ্যে প্রয়োগ করা যেতে পারে। এখানে একটি তাজা উদাহরণ:

class Grandparent:
    @property
    def culture(self):
        return "Family property"

    # add a setter method
    @culture.setter
    def culture(self, val):
        print('Fine, have your own culture')
        # overwrite the child class attribute
        type(self).culture = None
        self.culture = val

class Parent(Grandparent):
    pass

class Child(Parent):
    def __init__(self):
        self.culture = "I'm a millennial!"

c = Child()
print(c.culture)

যার ফলাফল:

Fine, have your own culture
I'm a millennial!

টি এ-Dah! উত্তরাধিকার সূত্রে প্রাপ্ত সম্পত্তিতে আপনি এখন নিজের উদাহরণ বৈশিষ্ট্যটি ওভাররাইট করতে পারেন!


তাহলে, সমস্যার সমাধান?

... আসলে তা না. এই পদ্ধতির সমস্যাটি হ'ল, এখন আপনার কাছে সঠিক setterপদ্ধতি থাকতে পারে না । এমন কিছু ঘটনা রয়েছে যেখানে আপনি নিজের মান নির্ধারণ করতে চান property। তবে এখন আপনি যখনই সেট করেন self.culture = ...এটি সর্বদা আপনার getter(যা এই উদাহরণস্বরূপ, কেবল @propertyআবৃত অংশ) এর মধ্যে সংজ্ঞায়িত যেকোন ক্রিয়াকে ওভাররাইট করে দেবে You আপনি আরও সংক্ষেপিত পদক্ষেপে যুক্ত করতে পারেন, তবে একটি উপায় বা অন্য উপায় এটি সর্বদা ন্যায়বিচারের চেয়ে বেশি জড়িত থাকবে self.culture = ...। উদাহরণ:

class Grandparent:
    # ...
    @culture.setter
    def culture(self, val):
        if isinstance(val, tuple):
            if val[1]:
                print('Fine, have your own culture')
                type(self).culture = None
                self.culture = val[0]
        else:
            raise AttributeError("Oh no you don't")

# ...

class Child(Parent):
    def __init__(self):
        try:
            # Usual setter
            self.culture = "I'm a Gen X!"
        except AttributeError:
            # Trigger the overwrite condition
            self.culture = "I'm a Boomer!", True

এটা waaaaay আরো অন্যান্য উত্তর চেয়ে জটিল, size = Noneবর্গ স্তরে।

আপনি এবং বা অতিরিক্ত পদ্ধতিগুলি পরিচালনা করার পরিবর্তে আপনার নিজের বর্ণনাকারী লেখার বিষয়টি বিবেচনা করতে পারেন । তবে দিনের শেষে, যখন রেফারেন্স হয়, সর্বদা সর্বদা প্রথমে ট্রিগার করা হবে এবং যখন রেফারেন্স করা হয়, সর্বদা প্রথমে ট্রিগার করা হবে। আমি যতদূর চেষ্টা করেছি তার আশেপাশে কোনও লাভ নেই।__get____set__self.culture__get__self.culture = ...__set__


ইস্যুটির জটিলতা, আইএমও

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

  1. আমি কি সত্যিই propertyএই জন্য একটি প্রয়োজন ?
  2. কোনও পদ্ধতি কি আমাকে অন্যভাবে সেবা করতে পারে?
  3. আমার যদি একটি দরকার propertyহয় তবে আমার এটির ওভাররাইট করার কোনও কারণ আছে কি?
  4. সাবক্লাসগুলি কি একই পরিবারে অন্তর্ভুক্ত থাকে যদি সেগুলি propertyপ্রয়োগ না হয়?
  5. আমার যদি কোনও / সবগুলি ওভাররাইটের প্রয়োজন হয় property, তবে একটি আলাদা পদ্ধতি কী আমাকে কেবল পুনরায় নিয়োগের চেয়ে আরও ভাল কাজ করবে, যেহেতু পুনরায় সাইন ইন করা দুর্ঘটনাক্রমে এসটিকে অকার্যকর করতে পারে property?

পঞ্চম পয়েন্টের জন্য, আমার পদ্ধতির overwrite_prop()বেস ক্লাসে এমন একটি পদ্ধতি থাকবে যা বর্তমান বর্গের বৈশিষ্ট্যটি ওভাররাইট করে যাতে propertyইচ্ছেটি আর ট্রিগার হয় না:

class Grandparent:
    # ...
    def overwrite_props(self):
        # reassign class attributes
        type(self).size = None
        type(self).len = None
        # other properties, if necessary

# ...

# Usage
class Child(Parent):
    def __init__(self):
        self.overwrite_props()
        self.size = 5
        self.len = 10

যেমন আপনি দেখতে পাচ্ছেন, কিছুটা দ্বিধা-দ্বন্দ্বে থাকলেও এটি কোনও ক্রিপ্টিকের চেয়ে কমপক্ষে আরও স্পষ্ট size = None। এটি বলেছিল, শেষ পর্যন্ত, আমি সম্পত্তিটি মোটেই ওভাররাইট করব না, এবং আমার নকশাটি মূল থেকে পুনর্বিবেচনা করব।

আপনি যদি এটিকে এ পর্যন্ত তৈরি করে থাকেন - আমার সাথে এই যাত্রাটি হাঁটার জন্য আপনাকে ধন্যবাদ। এটি একটি মজার ছোট অনুশীলন ছিল।


2
বাহ, অনেক অনেক ধন্যবাদ! এটি হজম করতে আমার কিছুটা সময় লাগবে, তবে আমি মনে করি আমি এটি চেয়েছি।
পল প্যানজার

6

@propertyশ্রেণীর স্তরে সংজ্ঞায়িত করা হয়। ডকুমেন্টেশন কিভাবে এটি কাজ করে উপর সম্পূর্ণ বিস্তারিত মধ্যে যায়, কিন্তু এটা যথেষ্ট অর্থাৎ সেটিং বা পেয়ে একটি নির্দিষ্ট পদ্ধতি কলিং মধ্যে সম্পত্তি সমাধান করুন। যাইহোক, propertyএই প্রক্রিয়াটি পরিচালনা করে এমন বস্তু শ্রেণীর নিজস্ব সংজ্ঞা দিয়ে সংজ্ঞায়িত করা হয়। এটি একটি শ্রেণি ভেরিয়েবল হিসাবে সংজ্ঞায়িত হলেও উদাহরণ ভেরিয়েবলের মতো আচরণ করে।

এর একটি পরিণতি হ'ল আপনি এটিকে ক্লাস পর্যায়ে নির্দ্বিধায় পুনরায় নিযুক্ত করতে পারেন :

print(Math_Set_Base.size)
# <property object at 0x10776d6d0>

Math_Set_Base.size = 4
print(Math_Set_Base.size)
# 4

এবং অন্য কোনও শ্রেণি-স্তরের নামের মতো (যেমন পদ্ধতিগুলি), আপনি এটিকে একটি সাবক্লাসে স্পষ্টভাবে আলাদাভাবে সংজ্ঞায়িত করে ওভাররাইড করতে পারেন:

class Square_Integers_Below(Math_Set_Base):
    # explicitly define size at the class level to be literally anything other than a @property
    size = None

    def __init__(self,cap):
        self.size = int(math.sqrt(cap))

print(Square_Integers_Below(4).size)  # 2
print(Square_Integers_Below.size)     # None

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


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

@ পলপাঞ্জার এই পেছনের পদ্ধতিগুলি আমি খতিয়ে দেখিনি, তাই আমি নিজেই আপনাকে সন্তোষজনক উত্তর দিতে পারলাম না। আপনি চাইলে সিপিথন উত্স কোড থেকে এটিকে ধাঁধা দেওয়ার চেষ্টা করতে পারেন। প্রক্রিয়াটি স্বয়ংক্রিয় করার ক্ষেত্রে, আমি মনে করি না যে এটি প্রথমে সম্পত্তি হিসাবে না তৈরি করা বা আপনার কোড পড়া পড়া লোকদের আপনি কী করছেন তা জানাতে কেবল একটি মন্তব্য / ডক্ট্রিং যুক্ত করা ছাড়া অন্য কোনও ভাল উপায় আছে there's । এরকম কিছু# declare size to not be a @property
সবুজ পোশাকওয়ালা গাই

4

আপনার মোটেও কোনও এসাইনমেন্টের দরকার নেই sizesizeবেস ক্লাসের একটি সম্পত্তি, তাই আপনি শিশু শ্রেণিতে সেই সম্পত্তিটিকে ওভাররাইড করতে পারেন:

class Math_Set_Base:
    @property
    def size(self):
        return len(self.elements)

    # size = property(lambda self: self.elements)


class Square_Integers_Below(Math_Set_Base):

    def __init__(self, cap):
        self._cap = cap

    @property
    def size(self):
        return int(math.sqrt(self._cap))

    # size = property(lambda self: int(math.sqrt(self._cap)))

আপনি (মাইক্রো) বর্গক্ষেত্রের পূর্বের দ্বারা এটি অনুকূল করতে পারেন:

class Square_Integers_Below(Math_Set_Base):

    def __init__(self, cap):
        self._size = int(math.sqrt(self._cap))

    @property
    def size(self):
        return self._size

এটি একটি দুর্দান্ত বিষয়, একটি সন্তানের সম্পত্তি সহ একটি পিতামাতার সম্পত্তি ওভাররাইড করুন! +1
r.ook

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

আমি নিশ্চিত নই কেন আপনি নিজের কোডটিকে এভাবে জটিল করতে চান। sizeএটি একটি সম্পত্তি এবং উদাহরণের বৈশিষ্ট্য নয় এমন স্বীকৃতির ছোট দামের জন্য আপনাকে অভিনব কিছু করতে হবে না ।
চিপনার

আমার ব্যবহারের ক্ষেত্রে প্রচুর গুণাবলীর সাথে প্রচুর শিশু শ্রেণি রয়েছে; এই সমস্তগুলির জন্য একটির পরিবর্তে 5 টি লাইন লেখার দরকার নেই, একটি নির্দিষ্ট পরিমাণ কল্পিততা প্রমাণ করে আইএমও।
পল প্যানজার

2

দেখে মনে হচ্ছে আপনি sizeনিজের ক্লাসে সংজ্ঞা দিতে চান :

class Square_Integers_Below(Math_Set_Base):
    size = None

    def __init__(self, cap):
        self.size = int(math.sqrt(cap))

আরেকটি বিকল্প হ'ল capআপনার ক্লাসে সঞ্চয় করা এবং এটি sizeসম্পত্তি হিসাবে সংজ্ঞায়িত হিসাবে গণনা করা (যা বেস শ্রেণীর সম্পত্তিকে ওভাররাইড করে size)।


2

আমি এর মতো একটি সেটার যুক্ত করার পরামর্শ দিচ্ছি:

class Math_Set_Base:
    @property
    def size(self):
        try:
            return self._size
        except:
            return len(self.elements)

    @size.setter
    def size(self, value):
        self._size = value

আপনি এভাবে ডিফল্ট .sizeসম্পত্তি ওভাররাইড করতে পারেন :

class Concrete_Math_Set(Math_Set_Base):
    def __init__(self,*elements):
        self.elements = elements

class Square_Integers_Below(Math_Set_Base):
    def __init__(self,cap):
        self.size = int(math.sqrt(cap))

print(Concrete_Math_Set(1,2,3).size) # 3
print(Square_Integers_Below(7).size) # 2

1

এছাড়াও আপনি পরবর্তী জিনিস করতে পারেন

class Math_Set_Base:
    _size = None

    def _size_call(self):
       return len(self.elements)

    @property
    def size(self):
        return  self._size if self._size is not None else self._size_call()

class Concrete_Math_Set(Math_Set_Base):
    def __init__(self, *elements):
        self.elements = elements


class Square_Integers_Below(Math_Set_Base):
    def __init__(self, cap):
        self._size = int(math.sqrt(cap))

এটি ঝরঝরে, তবে আপনার আসলে দরকার নেই _size বা _size_callসেখানে নেই। আপনি sizeশর্ত হিসাবে ফাংশন কলের মধ্যে বেকড থাকতে পারে , এবং অতিরিক্ত ক্লাসের রেফারেন্স ব্যবহার try... except... না করে পরীক্ষা করার জন্য ব্যবহার করতে পারেন _size। যদিও নির্বিশেষে, আমি অনুভব করি যে size = Noneএটি প্রথম স্থানে সরল ওভাররাইটিংয়ের চেয়ে আরও গুপ্ত ।
r.ook
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.