उपवर्ग: क्या किसी संपत्ति को एक पारंपरिक विशेषता से ओवरराइड करना संभव है?


14

चलिए मान लेते हैं कि हम उन वर्गों का एक परिवार बनाना चाहते हैं जो अलग-अलग कार्यान्वयन या एक अतिव्यापी अवधारणा के विशेषज्ञ हैं। मान लें कि कुछ व्युत्पन्न गुणों के लिए एक प्रशंसनीय डिफ़ॉल्ट कार्यान्वयन है। हम इसे बेस क्लास में रखना चाहते हैं

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मान्य भी है क्योंकि यह एक साधारण विशेषता है। इस विशेष "आकार" उपयोग के मामले में, यह akward प्रतीत हो सकता है (इसके बजाय एक संपत्ति का उपयोग करें), लेकिन सामान्य मामले में "उपवर्ग में एक गणना आसानी से ओवरराइड करें", यह कुछ मामलों में अच्छा हो सकता है। आप विशेषता अभिगम को भी नियंत्रित कर सकते हैं __setattr__()लेकिन यह भारी हो सकता है।
अर्केलिस

1
मैं इससे सहमत नहीं हूं कि इन के लिए वैध उपयोग का मामला हो सकता है, मैं सिर्फ कैविएट का उल्लेख करना चाहता था क्योंकि यह भविष्य में डिबगिंग प्रयासों को जटिल कर सकता है। जब तक आप और ओपी दोनों निहितार्थों से अवगत होंगे तब तक सब ठीक है।
आर।

10

यह एक लंबा घुमावदार जवाब होगा जो केवल प्रशंसात्मक होने के लिए काम कर सकता है ... लेकिन आपके प्रश्न ने मुझे खरगोश के छेद के नीचे की सवारी के लिए ले लिया, इसलिए मैं अपने निष्कर्षों (और दर्द) को भी साझा करना चाहूंगा।

आप अंततः इस जवाब को अपनी वास्तविक समस्या के लिए उपयोगी नहीं पा सकते हैं। वास्तव में, मेरा निष्कर्ष यह है कि - मैं ऐसा बिल्कुल नहीं करूंगा। कहा जाता है कि, इस निष्कर्ष की पृष्ठभूमि आपको थोड़ा मनोरंजन कर सकती है, क्योंकि आप अधिक विवरण की तलाश कर रहे हैं।


कुछ गलत धारणा को संबोधित करते हुए

ज्यादातर मामलों में सही होने के दौरान पहला उत्तर हमेशा ऐसा नहीं होता है। उदाहरण के लिए, इस वर्ग पर विचार करें:

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स्थान पहली बार में कहां परिभाषित किया गया है। यदि आपके @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. cultureवर्ग में मौजूद नहीं है Child.__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'

टा-दाह! 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!

टीए-डाह! अब आप विरासत में मिली संपत्ति पर अपनी खुद की विशेषता को अधिलेखित कर सकते हैं!


तो, समस्या हल हो गई?

... ज़रुरी नहीं। इस दृष्टिकोण के साथ समस्या है, अब आपके पास एक उचित setterतरीका नहीं हो सकता है । ऐसे मामले हैं जहां आप अपने मूल्यों को निर्धारित करना चाहते हैं property। लेकिन अब आप सेट जब भी self.culture = ...यह होगा हमेशा के ऊपर लिख जो कुछ भी कार्य करते हैं आप में परिभाषित किया गया getterहै (जो इस उदाहरण में, वास्तव में सिर्फ है @propertyलिपटे भाग। आप कर सकते हैं अधिक सूक्ष्म उपायों में जोड़ने के लिए, लेकिन एक तरह से या किसी अन्य रूप में यह हमेशा से ही अधिक से अधिक शामिल करेंगे 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का अर्थ है एक विवरणक की तरह getattrया जैसे तरीकों से सुविधाजनक पहुंच के साथ setattr। यदि आप भी इन तरीकों को एक अलग उद्देश्य को प्राप्त करना चाहते हैं, तो आप सिर्फ परेशानी के लिए पूछ रहे हैं। मैं शायद दृष्टिकोण पर पुनर्विचार करूंगा:

  1. क्या मुझे वास्तव में इसके लिए आवश्यकता है property?
  2. क्या कोई विधि मुझे अलग तरीके से परोस सकती है?
  3. अगर मुझे इसकी आवश्यकता है property, तो क्या कोई कारण है कि मुझे इसे अधिलेखित करने की आवश्यकता होगी?
  4. यदि ये propertyलागू नहीं होते हैं तो क्या उपवर्ग वास्तव में एक ही परिवार के हैं ?
  5. अगर मुझे किसी भी / सभी को अधिलेखित करने की आवश्यकता है property, तो क्या एक अलग विधि मुझे केवल पुन: असाइन करने की तुलना में बेहतर सेवा प्रदान करेगी, क्योंकि पुन: संयोगवश गलती से propertyएस शून्य हो सकता है ?

बिंदु 5 के लिए, मेरे दृष्टिकोण 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__? इसके अलावा, क्या इसे स्वचालित करने का कोई तरीका है? हां, यह सिर्फ एक पंक्ति है, लेकिन यह उस तरह की चीज है जो पढ़ने में बहुत गूढ़ है, यदि आप भविष्यवाणियों के विवरण से परिचित नहीं हैं आदि
पॉल पैंजर

@PaPPanzer मैंने इसके पीछे की प्रक्रियाओं पर ध्यान नहीं दिया है, इसलिए मैं आपको स्वयं एक संतोषजनक उत्तर नहीं दे सका। यदि आप चाहते हैं तो आप इसे cpython स्रोत कोड से पहेली बनाने की कोशिश कर सकते हैं। इस प्रक्रिया को स्वचालित करने के लिए, मुझे नहीं लगता कि ऐसा करने का एक अच्छा तरीका यह है कि इसके अलावा या तो यह पहली जगह में संपत्ति नहीं बना सकता है, या केवल एक टिप्पणी जोड़ रहा है / अपने कोड को पढ़ने वाले लोगों को यह बताने के लिए कि आप क्या कर रहे हैं । कुछ इस तरह# declare size to not be a @property
ग्रीन क्लॉक गाइ

4

आपको असाइनमेंट (से size) बिलकुल नहीं चाहिए । sizeबेस क्लास में एक प्रॉपर्टी है, इसलिए आप उस प्रॉपर्टी को चाइल्ड क्लास में ओवरराइड कर सकते हैं:

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

यह एक तरह से सीधी-सादी बात है, लेकिन मुझे इसमें दिलचस्पी थी और खासतौर पर उन तरीकों के बारे में पूछा, जिनके बारे में पूछते हुए कि एक गैर-प्रॉपर्टी ओवरराइड की अनुमति देती है, ताकि एक यादगार प्रॉपर्टी लिखने में असुविधा न हो, जहां साधारण असाइनमेंट करना होगा काम।
पॉल पैंजर

मुझे यकीन नहीं है कि आप इस तरह से अपने कोड को जटिल क्यों करना चाहेंगे। यह पहचानने 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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.