এটি একটি দীর্ঘ বাতাসের উত্তর হবে যা কেবল প্রশংসামূলক হতে পারে ... তবে আপনার প্রশ্নটি খরগোশের গর্তের নীচে চড়ার জন্য আমাকে গ্রহণ করেছিল যাতে আমি আমার অনুসন্ধানগুলি (এবং ব্যথা )ও ভাগ করে নিতে চাই।
আপনি শেষ পর্যন্ত এই উত্তরটি আপনার প্রকৃত সমস্যার পক্ষে সহায়ক নাও হতে পারেন। আসলে, আমার উপসংহারটি হ'ল - আমি এটি কিছু করব না 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>
কিভাবে লক্ষ করুন:
self.world_view
প্রয়োগ করতে সক্ষম হয়েছিল, self.culture
ব্যর্থ হওয়ার পরেও
culture
Child.__dict__
( mappingproxy
ক্লাসের মধ্যে উপস্থিত নেই , উদাহরণের সাথে বিভ্রান্ত হওয়ার দরকার নেই __dict__
)
- যদিও এটি
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
সম্পত্তিটিতে পুনর্নির্দিষ্ট করার পরে , আমরা তাত্ক্ষণিকভাবে উদাহরণ বৈশিষ্ট্যের অ্যাক্সেসটি হারাব।
অতএব, আমরা নিম্নলিখিত রেজোলিউশন অর্ডারটি মেনে নিতে পারি :
- একটি বর্গ অ্যাট্রিবিউট উপস্থিত থাকলে এবং এটি একটি হল
property
মাধ্যমে এর মান পুনরুদ্ধার getter
বা fget
(পরে এই বিষয়ে আরও)। বর্তমান শ্রেণি প্রথম থেকে বেস শ্রেণিতে সর্বশেষ।
- অন্যথায়, যদি কোনও উদাহরণ অ্যাট্রিবিউট উপস্থিত থাকে, উদাহরণ বৈশিষ্ট্যটির মানটি পুনরুদ্ধার করুন।
- অন্যথায়, শ্রেণিবদ্ধ
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
এর মতো বা ব্যবহার পদ্ধতি থেকে সুবিধাজনক অ্যাক্সেস সহকারে বর্ণনাকারীর মতো বোঝানো হয় । আপনি যদি এই পদ্ধতিগুলি কোনও অন্যরকম উদ্দেশ্য অর্জন করতে চান তবে আপনি কেবল সমস্যার জন্য বলছেন। আমি সম্ভবত পদ্ধতির পুনর্বিবেচনা করতে চাই:getattr
setattr
- আমি কি সত্যিই
property
এই জন্য একটি প্রয়োজন ?
- কোনও পদ্ধতি কি আমাকে অন্যভাবে সেবা করতে পারে?
- আমার যদি একটি দরকার
property
হয় তবে আমার এটির ওভাররাইট করার কোনও কারণ আছে কি?
- সাবক্লাসগুলি কি একই পরিবারে অন্তর্ভুক্ত থাকে যদি সেগুলি
property
প্রয়োগ না হয়?
- আমার যদি কোনও / সবগুলি ওভাররাইটের প্রয়োজন হয়
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
। এটি বলেছিল, শেষ পর্যন্ত, আমি সম্পত্তিটি মোটেই ওভাররাইট করব না, এবং আমার নকশাটি মূল থেকে পুনর্বিবেচনা করব।
আপনি যদি এটিকে এ পর্যন্ত তৈরি করে থাকেন - আমার সাথে এই যাত্রাটি হাঁটার জন্য আপনাকে ধন্যবাদ। এটি একটি মজার ছোট অনুশীলন ছিল।