উত্তর:
পাইথনে, এর উদ্দেশ্য
__slots__
কী এবং কে এগুলি এড়ানো উচিত?
বিশেষ বৈশিষ্ট্যটি __slots__
আপনাকে প্রত্যাশিত ফলাফল সহ আপনার অবজেক্টের দৃষ্টান্তগুলি যেমন প্রত্যাশিত বৈশিষ্ট্যের সাথে প্রত্যাশা করে, সেই উদাহরণটি আপনাকে স্পষ্টভাবে বলতে অনুমতি দেয়:
স্থান সঞ্চয় থেকে হয়
__dict__
।__dict__
এবং __weakref__
সৃষ্টি করা যদি পিতামাত্ত শ্রেণিগুলি তাদের অস্বীকার করে এবং আপনি ঘোষণা করেন __slots__
।ছোট সতর্কবাণী, আপনি কেবলমাত্র উত্তরাধিকার গাছে একটি নির্দিষ্ট স্লট একবার ঘোষণা করবেন। উদাহরণ স্বরূপ:
class Base:
__slots__ = 'foo', 'bar'
class Right(Base):
__slots__ = 'baz',
class Wrong(Base):
__slots__ = 'foo', 'bar', 'baz' # redundant foo and bar
পাইথন আপত্তি তোলে না যখন আপনি এই ভুলটি পেয়েছেন (এটি সম্ভবত হওয়া উচিত), সমস্যাগুলি অন্যথায় প্রকাশিত নাও হতে পারে, তবে আপনার অবজেক্টগুলি অন্যথায় যা করা উচিত তার চেয়ে বেশি স্থান গ্রহণ করবে। পাইথন ৩.৮:
>>> from sys import getsizeof
>>> getsizeof(Right()), getsizeof(Wrong())
(56, 72)
এটি কারণ বেসের স্লট বর্ণনাকারীর রং এর থেকে পৃথক একটি স্লট রয়েছে। এটি সাধারণত উঠে আসা উচিত নয়, তবে এটি হতে পারে:
>>> w = Wrong()
>>> w.foo = 'foo'
>>> Base.foo.__get__(w)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: foo
>>> Wrong.foo.__get__(w)
'foo'
বৃহত্তম ক্যাভিয়েট একাধিক উত্তরাধিকারের জন্য - একাধিক "Nompty স্লট সহ অভিভাবক ক্লাস" একত্রিত করা যায় না।
এই সীমাবদ্ধতা সামঞ্জস্য করার জন্য, সর্বোত্তম অভ্যাসগুলি অনুসরণ করুন: যথাযথভাবে তাদের কংক্রিট শ্রেণি এবং আপনার নতুন কংক্রিট শ্রেণি সম্মিলিতভাবে উত্তীর্ণ হবে এমন এক বা সমস্ত বাবা-মা'র বিমূর্ততা বাদে ফ্যাক্টর - বিমূর্ততা (গুলি) খালি স্লট প্রদান (ঠিক যেমন অ্যাবস্ট্রাক্ট বেস ক্লাসগুলিতে স্ট্যান্ডার্ড লাইব্রেরি)।
উদাহরণের জন্য নীচে একাধিক উত্তরাধিকারের বিভাগটি দেখুন।
এটির __slots__
পরিবর্তে স্লটে সংরক্ষণের জন্য নামযুক্ত বৈশিষ্ট্যগুলি থাকতে __dict__
, একটি শ্রেণীর অবশ্যই উত্তরাধিকার সূত্রে উত্তরাধিকারী object
।
ক এর সৃষ্টি রোধ করতে __dict__
আপনার উত্তরাধিকার হতে হবে object
এবং উত্তরাধিকারের সমস্ত শ্রেণীর অবশ্যই ঘোষণা __slots__
করতে হবে এবং তাদের '__dict__'
কোনওটির প্রবেশ থাকতে পারে না ।
আপনি পড়া চালিয়ে যেতে ইচ্ছুক হলে অনেকগুলি বিবরণ রয়েছে।
__slots__
: দ্রুত অ্যাট্রিবিউট অ্যাক্সেস।পাইথনের স্রষ্টা, গিডো ভ্যান রসুম বলেছেন যে তিনি আসলে __slots__
দ্রুত অ্যাট্রিবিউট অ্যাক্সেসের জন্য তৈরি করেছিলেন।
এটি পরিমাপযোগ্য তাৎপর্যযুক্ত দ্রুত অ্যাক্সেস প্রদর্শনের জন্য তুচ্ছ
import timeit
class Foo(object): __slots__ = 'foo',
class Bar(object): pass
slotted = Foo()
not_slotted = Bar()
def get_set_delete_fn(obj):
def get_set_delete():
obj.foo = 'foo'
obj.foo
del obj.foo
return get_set_delete
এবং
>>> min(timeit.repeat(get_set_delete_fn(slotted)))
0.2846834529991611
>>> min(timeit.repeat(get_set_delete_fn(not_slotted)))
0.3664822799983085
উবুন্টুতে পাইথন 3.5 তে স্লটড অ্যাক্সেস প্রায় 30% দ্রুত।
>>> 0.3664822799983085 / 0.2846834529991611
1.2873325658284342
উইন্ডোজ পাইথন 2 এ আমি এটি প্রায় 15% দ্রুত পরিমাপ করেছি।
__slots__
: মেমরি সঞ্চয়এর আরেকটি উদ্দেশ্য __slots__
হ'ল মেমরির স্থান হ্রাস করা যা প্রতিটি বস্তুর উদাহরণ গ্রহণ করে।
ডকুমেন্টেশনে আমার নিজের অবদানের পেছনের কারণগুলি স্পষ্টভাবে জানিয়েছে :
ব্যবহার করে সংরক্ষণ করা স্থানটি
__dict__
তাৎপর্যপূর্ণ হতে পারে।
এসকিউএলএলচেমি প্রচুর মেমরি সঞ্চয়কে দায়ী করে __slots__
।
এটি যাচাই করার জন্য, উবুন্টু লিনাক্সে পাইথন ২.7 এর অ্যানাকোন্ডা বিতরণ ব্যবহার করে guppy.hpy
(ওরফে হিপি) এবং sys.getsizeof
, __slots__
ঘোষিত ছাড়াই শ্রেণীর উদাহরণের আকার এবং অন্য কিছু নয়, 64৪ বাইট। এটি অন্তর্ভুক্ত করে না__dict__
। আবার অলস মূল্যায়নের জন্য পাইথনকে ধন্যবাদ __dict__
, রেফারেন্স না দেওয়া পর্যন্ত আপাতদৃষ্টিতে অস্তিত্বের জন্য ডাকা হয় না, তবে ডেটা ছাড়াই ক্লাসগুলি সাধারণত অকেজো হয়। যখন অস্তিত্ব হিসাবে ডাকা হয়, __dict__
গুণটি অতিরিক্তভাবে সর্বনিম্ন 280 বাইট হয়।
বিপরীতে, (কোনও ডেটা নেই) হিসাবে __slots__
ঘোষিত শ্রেণীর উদাহরণটি ()
কেবলমাত্র 16 বাইট এবং স্লটে একটি আইটেম সহ 56 টি বাইট, দুটি সহ।।।
Bit৪ বিট পাইথনের জন্য আমি পাইথন ২.7 এবং ৩.6 এর বাইটে মেমরির খরচটি চিত্রিত করি এবং ডিকটি ৩.6-তে বৃদ্ধি হয় যেখানে (০, ১, এবং ২ টি বৈশিষ্ট্য বাদে) প্রতিটি বিন্দুর জন্য __slots__
এবং __dict__
(কোনও স্লট সংজ্ঞায়িত হয়নি):
Python 2.7 Python 3.6
attrs __slots__ __dict__* __slots__ __dict__* | *(no slots defined)
none 16 56 + 272† 16 56 + 112† | †if __dict__ referenced
one 48 56 + 272 48 56 + 112
two 56 56 + 272 56 56 + 112
six 88 56 + 1040 88 56 + 152
11 128 56 + 1040 128 56 + 240
22 216 56 + 3344 216 56 + 408
43 384 56 + 3344 384 56 + 752
সুতরাং, পাইথন 3-তে ছোট ছোট ডিক্টস থাকা সত্ত্বেও, আমরা দেখতে পেলাম __slots__
আমাদের স্মৃতিশক্তি বাঁচানোর জন্য উদাহরণগুলির জন্য কত সুন্দরভাবে স্কেল করা হয়েছে এবং এটিই আপনি ব্যবহার করতে চান এটি একটি প্রধান কারণ __slots__
।
কেবলমাত্র আমার নোটগুলির সম্পূর্ণতার জন্য, নোট করুন যে ক্লাসের পাইথন 2 এর নেমস্পেসে এক সময়কালীন ব্যয় এবং পাইথন 3-তে 72 বাইট রয়েছে, কারণ স্লটগুলিতে "সদস্য" নামে পরিচিত বৈশিষ্ট্যের মতো ডেটা ডেস্ক্রিপ্টার ব্যবহার করা হয়।
>>> Foo.foo
<member 'foo' of 'Foo' objects>
>>> type(Foo.foo)
<class 'member_descriptor'>
>>> getsizeof(Foo.foo)
72
__slots__
:একটি তৈরির বিষয়টি অস্বীকার করতে __dict__
আপনাকে অবশ্যই সাবক্লাস করতে হবে object
:
class Base(object):
__slots__ = ()
এখন:
>>> b = Base()
>>> b.a = 'a'
Traceback (most recent call last):
File "<pyshell#38>", line 1, in <module>
b.a = 'a'
AttributeError: 'Base' object has no attribute 'a'
বা সংজ্ঞায়িত করে এমন অন্য শ্রেণীর সাবক্লাস __slots__
class Child(Base):
__slots__ = ('a',)
এবং এখন:
c = Child()
c.a = 'a'
কিন্তু:
>>> c.b = 'b'
Traceback (most recent call last):
File "<pyshell#42>", line 1, in <module>
c.b = 'b'
AttributeError: 'Child' object has no attribute 'b'
__dict__
স্লটেড অবজেক্টগুলিকে সাবক্লাসিং করার সময় সৃষ্টির অনুমতি দেওয়ার জন্য , কেবলমাত্র এতে যোগ '__dict__'
করুন __slots__
(স্লটগুলি অর্ডার করা হয়েছে তা নোট করুন এবং ইতিমধ্যে প্যারেন্ট ক্লাসে থাকা স্লটগুলি পুনরাবৃত্তি করা উচিত নয়):
class SlottedWithDict(Child):
__slots__ = ('__dict__', 'b')
swd = SlottedWithDict()
swd.a = 'a'
swd.b = 'b'
swd.c = 'c'
এবং
>>> swd.__dict__
{'c': 'c'}
অথবা আপনার এমনকি __slots__
আপনার সাবক্লাসে ঘোষণা করার দরকার নেই , এবং আপনি এখনও পিতামাতার কাছ থেকে স্লট ব্যবহার করবেন তবে একটি তৈরির সীমাবদ্ধ করবেন না __dict__
:
class NoSlots(Child): pass
ns = NoSlots()
ns.a = 'a'
ns.b = 'b'
এবং:
>>> ns.__dict__
{'b': 'b'}
তবে __slots__
একাধিক উত্তরাধিকারের জন্য সমস্যা সৃষ্টি করতে পারে:
class BaseA(object):
__slots__ = ('a',)
class BaseB(object):
__slots__ = ('b',)
কারণ খালি খালি স্লট উভয়ই পিতামাতার কাছ থেকে শিশু শ্রেণি তৈরি করা ব্যর্থ হয়:
>>> class Child(BaseA, BaseB): __slots__ = ()
Traceback (most recent call last):
File "<pyshell#68>", line 1, in <module>
class Child(BaseA, BaseB): __slots__ = ()
TypeError: Error when calling the metaclass bases
multiple bases have instance lay-out conflict
আপনি এ সমস্যার পাতিত করা, আপনি পারে শুধু অপসারণ __slots__
বাবা থেকে, অথবা আপনি বাবা নিয়ন্ত্রণ আছে যদি তাদের বিমূর্ত থেকে স্লট, অথবা refactor খালি করা উচিত:
from abc import ABC
class AbstractA(ABC):
__slots__ = ()
class BaseA(AbstractA):
__slots__ = ('a',)
class AbstractB(ABC):
__slots__ = ()
class BaseB(AbstractB):
__slots__ = ('b',)
class Child(AbstractA, AbstractB):
__slots__ = ('a', 'b')
c = Child() # no problem!
'__dict__'
করার জন্য __slots__
গতিশীল নিয়োগ পেতে:class Foo(object):
__slots__ = 'bar', 'baz', '__dict__'
এবং এখন:
>>> foo = Foo()
>>> foo.boink = 'boink'
সুতরাং '__dict__'
স্লটগুলির সাথে আমরা গতিশীল কার্যনির্বাহী হওয়া এবং এখনও আমরা যে নামগুলি প্রত্যাশা করি তার জন্য স্লট থাকার উল্টো দিক দিয়ে কিছু আকারের সুবিধাগুলি হারাব।
যখন আপনি কোনো বস্তু slotted নয় থেকে উত্তরাধিকারী, আপনি শব্দার্থবিদ্যা যখন আপনি ব্যবহার একই সাজানোর পেতে __slots__
নাম যে আছে - __slots__
, slotted মান বিন্দু যখন অন্য কোন মান উদাহরণস্বরূপ এর রাখা হয় __dict__
।
__slots__
এড়ানোর কারণ আপনি ফ্লাইতে অ্যাট্রিবিউটস যুক্ত করতে সক্ষম হতে চান তা আসলে কোনও ভাল কারণ নয় - এটির প্রয়োজন হলে কেবল "__dict__"
আপনার যুক্ত করুন __slots__
।
আপনার যদি সেই বৈশিষ্ট্যটির প্রয়োজন হয় তবে __weakref__
আপনি একইভাবে __slots__
স্পষ্টভাবে যুক্ত করতে পারেন।
নামকরণকৃত বিল্টিন অপরিবর্তনীয় দৃষ্টান্ত তৈরি করে যা খুব হালকা ওজনের হয় (মূলত: টিপলসের আকার) তবে সুবিধাগুলি পেতে আপনার নিজের প্রয়োজন যদি আপনি সেগুলি সাবক্লাস করেন:
from collections import namedtuple
class MyNT(namedtuple('MyNT', 'bar baz')):
"""MyNT is an immutable and lightweight object"""
__slots__ = ()
ব্যবহার:
>>> nt = MyNT('bar', 'baz')
>>> nt.bar
'bar'
>>> nt.baz
'baz'
এবং একটি অপ্রত্যাশিত বৈশিষ্ট্য নির্ধারণের চেষ্টা একটি উত্থাপন করে AttributeError
কারণ আমরা এর সৃষ্টি প্রতিরোধ করেছি __dict__
:
>>> nt.quux = 'quux'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'MyNT' object has no attribute 'quux'
আপনি ছাড়ার মাধ্যমে সৃষ্টির অনুমতি দিতে পারেন , তবে আপনি টিপলের সাব টাইপগুলি সহ খালিটি ব্যবহার করতে পারবেন না ।__dict__
__slots__ = ()
__slots__
এমনকি খালি খালি স্লট একাধিক পিতা-মাতার জন্য একই থাকলেও সেগুলি একসাথে ব্যবহার করা যায় না:
class Foo(object):
__slots__ = 'foo', 'bar'
class Bar(object):
__slots__ = 'foo', 'bar' # alas, would work if empty, i.e. ()
>>> class Baz(Foo, Bar): pass
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Error when calling the metaclass bases
multiple bases have instance lay-out conflict
__slots__
পিতামাতার মধ্যে একটি খালি ব্যবহার করা সর্বাধিক নমনীয়তা সরবরাহ করে বলে মনে হয়, বাচ্চাকে বাধা দেওয়ার বা অনুমতি দেওয়ার জন্য বাছাই করতে দেওয়া ( '__dict__'
গতিশীল কার্যনির্বাহীকরণ যোগ করে, উপরের অংশটি দেখুন) একটিটির সৃষ্টি__dict__
:
class Foo(object): __slots__ = ()
class Bar(object): __slots__ = ()
class Baz(Foo, Bar): __slots__ = ('foo', 'bar')
b = Baz()
b.foo, b.bar = 'foo', 'bar'
আপনার স্লট থাকতে হবে না - সুতরাং আপনি যদি এগুলি যুক্ত করেন এবং পরে এগুলি সরিয়ে থাকেন তবে এটি কোনও সমস্যা না করে।
এখানে একটি অঙ্গ নিয়ে বেরোন : আপনি যদি মিক্সিনগুলি রচনা করেন বা বিমূর্ত বেস ক্লাসগুলি ব্যবহার করেন , যা ইনস্ট্যান্ট করার উদ্দেশ্যে নয়, তবে __slots__
তাদের পিতামাতার একটি ফাঁকা সাবক্ল্যাসারদের জন্য নমনীয়তার দিক থেকে যাওয়ার সেরা উপায় বলে মনে হয়।
প্রদর্শনের জন্য, প্রথমে কোড সহ একটি শ্রেণি তৈরি করি যা আমরা একাধিক উত্তরাধিকারের অধীনে ব্যবহার করতে চাই
class AbstractBase:
__slots__ = ()
def __init__(self, a, b):
self.a = a
self.b = b
def __repr__(self):
return f'{type(self).__name__}({repr(self.a)}, {repr(self.b)})'
প্রত্যাশিত স্লট উত্তরাধিকার সূত্রে এবং ঘোষণা করে আমরা উপরেরগুলি সরাসরি ব্যবহার করতে পারি:
class Foo(AbstractBase):
__slots__ = 'a', 'b'
তবে আমরা এটির যত্ন নিই না, এটি তুচ্ছ একক উত্তরাধিকারী, আমাদের আরও একটি শ্রেণি প্রয়োজন যা থেকে আমরা উত্তরাধিকার সূত্রে পেলাম, সম্ভবত কোনও শোরগোল গুণ সহ:
class AbstractBaseC:
__slots__ = ()
@property
def c(self):
print('getting c!')
return self._c
@c.setter
def c(self, arg):
print('setting c!')
self._c = arg
এখন যদি উভয় ঘাঁটিতে অযৌক্তিক স্লট থাকে তবে আমরা নীচেরটি করতে পারিনি। (প্রকৃতপক্ষে, আমরা যদি চাইতাম তবে আমরা AbstractBase
ক্রেডিট স্লট a এবং b দিতে পারতাম এবং সেগুলি নীচের ঘোষণার বাইরে রেখে দিতাম - এগুলিকে ছেড়ে দেওয়া ভুল হত):
class Concretion(AbstractBase, AbstractBaseC):
__slots__ = 'a b _c'.split()
এবং এখন আমাদের একাধিক উত্তরাধিকারের মাধ্যমে উভয় থেকেই কার্যকারিতা রয়েছে এবং এখনও তা অস্বীকার করতে __dict__
এবং __weakref__
তাত্ক্ষণিক করতে পারি :
>>> c = Concretion('a', 'b')
>>> c.c = c
setting c!
>>> c.c
getting c!
Concretion('a', 'b')
>>> c.d = 'd'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Concretion' object has no attribute 'd'
__class__
স্লট বিন্যাসটি অভিন্ন না হলে আপনি যখন অন্য শ্রেণীর সাথে অ্যাসাইনমেন্টটি সম্পাদন করতে চান সেগুলি এড়িয়ে চলুন you (কে এটি করছে এবং কেন করছে তা শিখতে আমি খুব আগ্রহী))আপনি বাকী __slots__
ডকুমেন্টেশন (৩. dev ডিভ ডক্স সর্বাধিক বর্তমান) থেকে আরও গুপ্তচরকে জ্বালাতন করতে সক্ষম হতে পারেন , যার জন্য আমি সাম্প্রতিক অবদান রেখেছি।
বর্তমান শীর্ষ উত্তরগুলি পুরানো তথ্যের উদ্ধৃতি দেয় এবং বেশ হস্ত-avyেউযুক্ত এবং কিছু গুরুত্বপূর্ণ উপায়ে চিহ্নটি মিস করে।
__slots__
প্রচুর অবজেক্ট ইনস্ট্যান্ট করার সময় ব্যবহার করবেন না "আমি উদ্ধৃতি:
"আপনি
__slots__
যদি একই শ্রেণীর অনেকগুলি (শত, হাজার) বস্তু ইনস্ট্যান্ট করতে যাচ্ছেন তবে আপনি ব্যবহার করতে চান " "
অ্যাবস্ট্রাক্ট বেস ক্লাস, উদাহরণস্বরূপ, collections
মডিউল থেকে , তাত্ক্ষণিকভাবে নয়, তবুও __slots__
তাদের জন্য ঘোষিত হয়।
কেন?
যদি কোনও ব্যবহারকারী অস্বীকার করতে __dict__
বা __weakref__
তৈরি করতে চান তবে সেই জিনিসগুলি অবশ্যই পিতামাতার ক্লাসে উপলভ্য নয়।
__slots__
ইন্টারফেস বা মিক্সিন তৈরি করার সময় পুনরায় ব্যবহারযোগ্যতায় অবদান রাখে।
এটি সত্য যে অনেক পাইথন ব্যবহারকারী পুনরায় ব্যবহারযোগ্যতার জন্য লেখেন না, তবে আপনি যখন হন, অপ্রয়োজনীয় স্থান ব্যবহার অস্বীকার করার বিকল্প থাকা মূল্যবান।
__slots__
পিকিং ভাঙে নাএকটি স্লটেড বস্তু বাছাই করার সময়, আপনি এটি একটি বিভ্রান্তিকর অভিযোগ করতে পারেন TypeError
:
>>> pickle.loads(pickle.dumps(f))
TypeError: a class that defines __slots__ without defining __getstate__ cannot be pickled
এটি আসলে ভুল। এই বার্তাটি প্রাচীনতম প্রোটোকল থেকে এসেছে, এটি ডিফল্ট। আপনি -1
যুক্তি দিয়ে সর্বশেষ প্রোটোকলটি নির্বাচন করতে পারেন । পাইথন ২.7 এ এটি হবে 2
(যা ২.৩ সালে প্রবর্তিত হয়েছিল) এবং এটি ৩.6-এ রয়েছে 4
।
>>> pickle.loads(pickle.dumps(f, -1))
<__main__.Foo object at 0x1129C770>
পাইথন ২.7 এ:
>>> pickle.loads(pickle.dumps(f, 2))
<__main__.Foo object at 0x1129C770>
পাইথন ৩.6
>>> pickle.loads(pickle.dumps(f, 4))
<__main__.Foo object at 0x1129C770>
সুতরাং আমি এটি মনে রাখব, কারণ এটি একটি সমাধান সমস্যা।
প্রথম অনুচ্ছেদটি অর্ধেক সংক্ষিপ্ত ব্যাখ্যা, অর্ধেক ভবিষ্যদ্বাণীমূলক। এখানে একমাত্র অংশ যা আসলে প্রশ্নের উত্তর দেয়
এর যথাযথ ব্যবহার
__slots__
হ'ল বস্তুগুলিতে স্থান বাঁচানো। যে কোনও সময়ে বস্তুর সাথে অ্যাট্রিবিউট যুক্ত করার অনুমতি দেয় এমন একটি গতিশীল ডিক থাকার পরিবর্তে, একটি স্থিতিশীল কাঠামো রয়েছে যা তৈরির পরে সংযোজনকে অনুমতি দেয় না। এটি স্লট ব্যবহার করে এমন প্রতিটি বস্তুর জন্য একটি ডিকের ওভারহেড সংরক্ষণ করে
দ্বিতীয়ার্ধটি ইচ্ছাকৃত চিন্তাভাবনা, এবং ছাপ ছাড়াই:
যদিও এটি কখনও কখনও দরকারী অপ্টিমাইজেশন হয় তবে পাইথন ইন্টারপ্রেটার পর্যাপ্ত গতিশীল হলে এটি কেবল অপ্রয়োজনীয় হবে যাতে যখন বস্তুটিতে আসলে সংযোজন ছিল তখন কেবল ডিকের প্রয়োজন হবে।
পাইথন আসলে এর অনুরূপ কিছু করে, __dict__
এটি অ্যাক্সেস হওয়ার পরে কেবল তৈরি করে , তবে কোনও ডেটা ছাড়াই প্রচুর অবজেক্ট তৈরি করা মোটামুটি হাস্যকর।
দ্বিতীয় অনুচ্ছেদটি এড়াতে প্রকৃত কারণগুলিকে ছাড়িয়ে যায় এবং মিস করে __slots__
। নিচে না একটি আসল কারণ স্লট এড়াতে (জন্য প্রকৃত কারণ উপরে আমার উত্তর বাকি দেখুন।):
তারা নিয়ন্ত্রণের শৌখিনতা এবং স্ট্যাটিক টাইপিং ওয়েইনিজ দ্বারা আপত্তিজনকভাবে ব্যবহার করা যেতে পারে এমনভাবে স্লটযুক্ত বস্তুর আচরণ পরিবর্তন করে।
এরপরে পাইথনের সাথে সেই বিকৃত লক্ষ্য অর্জনের অন্যান্য উপায়গুলি নিয়ে আলোচনা করা যায়, এর সাথে কিছু করার বিষয়ে আলোচনা করা হয় না __slots__
।
তৃতীয় অনুচ্ছেদটি আরও ইচ্ছুক চিন্তাভাবনা। একসাথে এটি বেশিরভাগ দ্য মার্ক-কন্টেন্ট যা উত্তরদাতা এমনকি লেখকও করেনি এবং সাইটের সমালোচকদের জন্য গোলাবারুদে অবদান রাখে।
কিছু সাধারণ অবজেক্ট এবং স্লটেড অবজেক্ট তৈরি করুন:
>>> class Foo(object): pass
>>> class Bar(object): __slots__ = ()
এর মধ্যে দশ লক্ষ ইনস্ট্যান্ট করুন:
>>> foos = [Foo() for f in xrange(1000000)]
>>> bars = [Bar() for b in xrange(1000000)]
এর সাথে পরিদর্শন করুন guppy.hpy().heap()
:
>>> guppy.hpy().heap()
Partition of a set of 2028259 objects. Total size = 99763360 bytes.
Index Count % Size % Cumulative % Kind (class / dict of class)
0 1000000 49 64000000 64 64000000 64 __main__.Foo
1 169 0 16281480 16 80281480 80 list
2 1000000 49 16000000 16 96281480 97 __main__.Bar
3 12284 1 987472 1 97268952 97 str
...
নিয়মিত জিনিস এবং তাদের অ্যাক্সেস করুন __dict__
এবং আবার পরীক্ষা করুন:
>>> for f in foos:
... f.__dict__
>>> guppy.hpy().heap()
Partition of a set of 3028258 objects. Total size = 379763480 bytes.
Index Count % Size % Cumulative % Kind (class / dict of class)
0 1000000 33 280000000 74 280000000 74 dict of __main__.Foo
1 1000000 33 64000000 17 344000000 91 __main__.Foo
2 169 0 16281480 4 360281480 95 list
3 1000000 33 16000000 4 376281480 99 __main__.Bar
4 12284 0 987472 0 377268952 99 str
...
এটি পাইথনের ইতিহাসের সাথে সামঞ্জস্যপূর্ণ, পাইথন ২.২-এ একীকরণের ধরণ এবং ক্লাসগুলি থেকে
যদি আপনি একটি অন্তর্নির্মিত ধরণের সাবক্লাস করেন তবে অতিরিক্ত স্থান স্বয়ংক্রিয়ভাবে সংস্থান করার জন্য দৃষ্টান্তগুলিতে যুক্ত হবে
__dict__
এবং__weakrefs__
। (__dict__
আপনি এটি ব্যবহার না করা অবধি এটি আরম্ভ করা হয়নি, সুতরাং আপনার তৈরি প্রতিটি উদাহরণের জন্য খালি অভিধান দ্বারা দখল করা স্থান সম্পর্কে আপনার চিন্তা করা উচিত নয়)) আপনার যদি এই অতিরিক্ত স্থানের প্রয়োজন না হয় তবে আপনি "__slots__ = []
" বাক্যাংশটি যোগ করতে পারেন তোমার শ্রেণী.
__slots__
। সিরিয়াসলি! ধন্যবাদ!
জ্যাকব হ্যালেনের উদ্ধৃতি :
এর যথাযথ ব্যবহার
__slots__
হ'ল বস্তুগুলিতে স্থান বাঁচানো। যে কোনও সময়ে বস্তুর সাথে অ্যাট্রিবিউট যুক্ত করার অনুমতি দেয় এমন একটি গতিশীল ডিক থাকার পরিবর্তে, একটি স্থিতিশীল কাঠামো রয়েছে যা তৈরির পরে সংযোজনকে অনুমতি দেয় না। [এটি ব্যবহার__slots__
প্রতিটি বস্তুর জন্য একটি ডিকের ওভারহেডকে সরিয়ে দেয়]] যদিও এটি কখনও কখনও দরকারী অপ্টিমাইজেশন হয় তবে এটি সম্পূর্ণ অপ্রয়োজনীয় হবে যদি পাইথন ইন্টারপ্রেটার যথেষ্ট গতিশীল ছিল যাতে এটি কেবল ডিকের প্রয়োজন যখন সেখানে সত্যিকারের সংযোজনগুলি ছিল অবজেক্ট।দুর্ভাগ্যক্রমে স্লটে একটি পার্শ্ব প্রতিক্রিয়া আছে। তারা নিয়ন্ত্রণের শৌখিনতা এবং স্ট্যাটিক টাইপিং ওয়েইনিজ দ্বারা আপত্তিজনকভাবে ব্যবহার করা যেতে পারে এমনভাবে স্লটযুক্ত বস্তুর আচরণ পরিবর্তন করে। এটি খারাপ, কারণ কন্ট্রোল ফ্রিক্সগুলি মেটাক্লাসগুলিকে অপব্যবহার করা উচিত এবং স্ট্যাটিক টাইপিং ওয়েনিগুলি সজ্জাকারীদের আপত্তিজনক হওয়া উচিত, যেহেতু পাইথনে, কিছু করার একমাত্র স্পষ্ট উপায় থাকা উচিত।
সিপিথনকে ছাড়াই স্থান সাশ্রয় করার জন্য যথেষ্ট স্মার্ট
__slots__
করা একটি বড় উদ্যোগ গ্রহণ, যার কারণ সম্ভবত এটি পি 3 কে (এখনও) এর পরিবর্তনের তালিকায় নেই।
__slots__
স্থির টাইপিংয়ের মতো একই সমস্যাগুলিকে সম্বোধন করে না। উদাহরণস্বরূপ, সি ++ এ এটি কোনও সদস্যের ভেরিয়েবলের ঘোষণাকে সীমাবদ্ধ করা হচ্ছে না, এটি সেই ভেরিয়েবলের জন্য একটি অযৌক্তিক ধরণের (এবং সংকলক প্রয়োগ করা) অ্যাসাইনমেন্ট। আমি __slots__
কেবল কথোপকথনে আগ্রহী, এর ব্যবহারকে সমর্থন করছি না । ধন্যবাদ!
আপনি __slots__
যদি একই শ্রেণীর অনেকগুলি (শত, হাজার) বস্তু ইনস্ট্যান্ট করতে যাচ্ছেন তবে আপনি ব্যবহার করতে চান । __slots__
কেবল একটি মেমরি অপ্টিমাইজেশন সরঞ্জাম হিসাবে উপস্থিত।
__slots__
এট্রিবিউট তৈরিতে বাধা দেওয়ার জন্য এটি ব্যবহার করতে অত্যন্ত নিরুৎসাহিত করা হয়েছে ।
__slots__
ডিফল্ট (প্রাচীনতম) আচার প্রোটোকল দিয়ে কাজ করবে না এমন জিনিসগুলি বাছাই করা ; পরবর্তী সংস্করণ নির্দিষ্ট করা দরকার।
পাইথনের অন্যান্য কিছু অন্তঃসংশোধন বৈশিষ্ট্যগুলিও বিরূপ প্রভাবিত হতে পারে।
প্রতিটি পাইথন অবজেক্টের একটি __dict__
অ্যাট্রিবিউট থাকে যা একটি অভিধান যা অন্যান্য সমস্ত বৈশিষ্ট্যযুক্ত। উদাহরণস্বরূপ, আপনি self.attr
পাইথন টাইপ করার সময় আসলে কাজ করে self.__dict__['attr']
। যেহেতু আপনি কল্পনা করতে পারেন অ্যাট্রিবিউটিকে সঞ্চয় করতে কোনও অভিধান ব্যবহার করার জন্য এটি অ্যাক্সেস করার জন্য কিছু অতিরিক্ত জায়গা এবং সময় লাগে।
তবে, আপনি যখন ব্যবহার করবেন তখন __slots__
class শ্রেণীর জন্য তৈরি যে কোনও বস্তুর কোনও __dict__
বৈশিষ্ট্য থাকবে না । পরিবর্তে, সমস্ত অ্যাট্রিবিউট অ্যাক্সেস পয়েন্টারগুলির মাধ্যমে সরাসরি করা হয়।
সুতরাং যদি আপনি একটি পূর্ণ বর্গের পরিবর্তে কোনও সি স্টাইলের কাঠামো চান তবে আপনি __slots__
অবজেক্টগুলির আকার কমপ্যাক্ট করতে এবং অ্যাট্রিবিউট অ্যাক্সেসের সময় কমাতে ব্যবহার করতে পারেন । একটি ভাল উদাহরণ x ও y বৈশিষ্ট্যযুক্ত একটি পয়েন্ট শ্রেণি। আপনার যদি অনেকগুলি পয়েন্ট থাকে তবে আপনি __slots__
কিছু স্মৃতি সংরক্ষণ করতে চেষ্টা করতে পারেন ।
__slots__
সংজ্ঞায়িত সহ শ্রেণীর উদাহরণ সি-স্টাইলের কাঠামোর মতো নয় । সূচকগুলিতে অ্যাট্রিবিউটের নাম ম্যাপিংয়ের জন্য একটি শ্রেণির স্তরের অভিধান রয়েছে, অন্যথায় নিম্নলিখিতগুলি সম্ভব হবে না: class A(object): __slots__= "value",\n\na=A(); setattr(a, 'value', 1)
আমি সত্যিই মনে করি এই উত্তরটি পরিষ্কার করা উচিত (আপনি চাইলে আমি এটি করতে পারি)। এছাড়াও, আমি নিশ্চিত না যে এর instance.__hidden_attributes[instance.__class__[attrname]]
চেয়ে দ্রুত instance.__dict__[attrname]
।
অন্যান্য উত্তরগুলির পাশাপাশি, এখানে ব্যবহারের উদাহরণ __slots__
:
>>> class Test(object): #Must be new-style class!
... __slots__ = ['x', 'y']
...
>>> pt = Test()
>>> dir(pt)
['__class__', '__delattr__', '__doc__', '__getattribute__', '__hash__',
'__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__',
'__repr__', '__setattr__', '__slots__', '__str__', 'x', 'y']
>>> pt.x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: x
>>> pt.x = 1
>>> pt.x
1
>>> pt.z = 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Test' object has no attribute 'z'
>>> pt.__dict__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Test' object has no attribute '__dict__'
>>> pt.__slots__
['x', 'y']
সুতরাং, বাস্তবায়নের জন্য __slots__
, এটি কেবল একটি অতিরিক্ত লাইন লাগে (এবং আপনার ক্লাসটি যদি এটি ইতিমধ্যে না থাকে তবে এটি নতুন শৈলীর শ্রেণি তৈরি করে)। এইভাবে আপনি এই শ্রেণীর মেমরির পদচিহ্নগুলি 5-গুণ কমাতে পারবেন, কাস্টম পিকেল কোডটি লিখতে হবে এবং যদি তা কখন প্রয়োজনীয় হয়।
ফাংশন কল করার সময় "নামযুক্ত পদ্ধতি প্রেরণ" মুছে ফেলার জন্য লাইব্রেরি কলগুলির জন্য স্লটগুলি খুব দরকারী। এসডাব্লুআইজি ডকুমেন্টেশনে এটি উল্লেখ করা হয়েছে । স্লট ব্যবহার করে সাধারণত ডাকা ফাংশনগুলির জন্য ফাংশন ওভারহেডকে হ্রাস করতে চায় এমন উচ্চ কার্য সম্পাদনের লাইব্রেরিগুলির জন্য much
এখন এটি সরাসরি ওপিএস প্রশ্নের সাথে সম্পর্কিত নাও হতে পারে। এটি কোনও বস্তুর স্লট সিনট্যাক্স ব্যবহারের চেয়ে এক্সটেনশানগুলি বাড়ানোর সাথে সম্পর্কিত । তবে এটি স্লট এবং তাদের পিছনে কিছু যুক্তির ব্যবহারের জন্য ছবিটি সম্পূর্ণ করতে সহায়তা করে।
একটি শ্রেণীর উদাহরণের একটি বৈশিষ্ট্যের 3 টি বৈশিষ্ট্য রয়েছে: উদাহরণ, বৈশিষ্ট্যের নাম এবং বৈশিষ্ট্যের মান the
ইন নিয়মিত অ্যাট্রিবিউট এক্সেস , উদাহরণস্বরূপ একটি অভিধান হিসাবে কাজ করে এবং যে অভিধানে কী মান আপ খুঁজছেন অ্যাট্রিবিউট নাম কাজ করে।
উদাহরণ (বৈশিষ্ট্য) -> মান
ইন __slots__ এক্সেস , গুণ নাম অভিধান হিসাবে কাজ করে এবং উদাহরণ হিসেবে বলা যায় কাজ যেমন অভিধানে কী মান আপ খুঁজছেন।
বৈশিষ্ট্য (উদাহরণ) -> মান
ইন ফ্লাইওয়েট প্যাটার্ন , গুণ নাম অভিধান হিসাবে কাজ করে এবং মান যে অভিধানে কী উদাহরণস্বরূপ আপ খুঁজছেন হিসাবে কাজ করে।
বৈশিষ্ট্য (মান) -> উদাহরণ
__slots__
?
__slot__
গুণাবলীর খুব সাধারণ উদাহরণ
__slots__
__slot__
আমার ক্লাসে যদি অ্যাট্রিবিউট না থাকে তবে আমি আমার অবজেক্টগুলিতে নতুন অ্যাট্রিবিউট যুক্ত করতে পারি।
class Test:
pass
obj1=Test()
obj2=Test()
print(obj1.__dict__) #--> {}
obj1.x=12
print(obj1.__dict__) # --> {'x': 12}
obj1.y=20
print(obj1.__dict__) # --> {'x': 12, 'y': 20}
obj2.x=99
print(obj2.__dict__) # --> {'x': 99}
আপনি যদি উপরের উদাহরণটির দিকে তাকান তবে দেখতে পাবেন যে 1 জ 1 এবং অজ 2 এর নিজস্ব x এবং y বৈশিষ্ট্য রয়েছে এবং পাইথন dict
প্রতিটি বস্তুর জন্য একটি বৈশিষ্ট্যও তৈরি করেছে ( اعتراض 1 এবং অবজেক্ট 2 )।
ধরুন আমার ক্লাস টেস্টে যদি এমন হাজার হাজার বস্তু থাকে? dict
প্রতিটি বস্তুর জন্য অতিরিক্ত বৈশিষ্ট্য তৈরি করা আমার কোডে প্রচুর ওভারহেড (মেমরি, কম্পিউটিং শক্তি ইত্যাদি) তৈরি করবে।
__slots__
এখন নিম্নলিখিত উদাহরণে আমার ক্লাস টেস্টের__slots__
বৈশিষ্ট্য রয়েছে । এখন আমি আমার অবজেক্টগুলিতে নতুন বৈশিষ্ট্য যুক্ত করতে পারি না (অ্যাট্রিবিউট ব্যতীত x
) এবং পাইথন dict
আর কোনও অ্যাট্রিবিউট তৈরি করে না । এটি প্রতিটি বস্তুর জন্য ওভারহেডকে সরিয়ে দেয়, যা আপনার কাছে অনেকগুলি অবজেক্ট থাকলে তা তাৎপর্যপূর্ণ হতে পারে।
class Test:
__slots__=("x")
obj1=Test()
obj2=Test()
obj1.x=12
print(obj1.x) # --> 12
obj2.x=99
print(obj2.x) # --> 99
obj1.y=28
print(obj1.y) # --> AttributeError: 'Test' object has no attribute 'y'
এর অপর কিছুটা অস্পষ্ট ব্যবহার __slots__
হ'ল প্রক্সিটাইপস প্যাকেজ থেকে কোনও বস্তুর প্রক্সিটির বৈশিষ্ট্য যুক্ত করা, এটি পিইএকে প্রকল্পের পূর্ববর্তী অংশ। এটি ObjectWrapper
আপনাকে অন্য কোনও বস্তুর প্রক্সি দেওয়ার অনুমতি দেয় তবে প্রক্সড অবজেক্টের সাথে সমস্ত মিথস্ক্রিয়াটিকে বিরত রাখে। এটি খুব সাধারণভাবে ব্যবহৃত হয় না (এবং পাইথন 3 সমর্থন নয়) তবে থ্রেডাডো ভিত্তিক একটি অ্যাসিঙ্ক প্রয়োগের চারপাশে থ্রেড-নিরাপদ ব্লকিংয়ের মোড়কে কার্যকর করার জন্য আমরা এটি ব্যবহার করেছি যা থ্রেড-সেফটি ব্যবহার করে আইলোপের মাধ্যমে প্রক্সাইড অবজেক্টের সমস্ত অ্যাক্সেস বাউন্স করে that concurrent.Future
সিঙ্ক্রোনাইজ করার জন্য ফলাফলগুলি এবং ফলাফলগুলি প্রত্যাবর্তন করে।
ডিফল্টরূপে প্রক্সি অবজেক্টে কোনও অ্যাট্রিবিউট অ্যাক্সেস আপনাকে প্রক্সিযুক্ত বস্তু থেকে ফলাফল দেবে। যদি আপনাকে প্রক্সি অবজেক্টে একটি অ্যাট্রিবিউট যুক্ত __slots__
করতে হয় তবে ব্যবহার করা যেতে পারে।
from peak.util.proxies import ObjectWrapper
class Original(object):
def __init__(self):
self.name = 'The Original'
class ProxyOriginal(ObjectWrapper):
__slots__ = ['proxy_name']
def __init__(self, subject, proxy_name):
# proxy_info attributed added directly to the
# Original instance, not the ProxyOriginal instance
self.proxy_info = 'You are proxied by {}'.format(proxy_name)
# proxy_name added to ProxyOriginal instance, since it is
# defined in __slots__
self.proxy_name = proxy_name
super(ProxyOriginal, self).__init__(subject)
if __name__ == "__main__":
original = Original()
proxy = ProxyOriginal(original, 'Proxy Overlord')
# Both statements print "The Original"
print "original.name: ", original.name
print "proxy.name: ", proxy.name
# Both statements below print
# "You are proxied by Proxy Overlord", since the ProxyOriginal
# __init__ sets it to the original object
print "original.proxy_info: ", original.proxy_info
print "proxy.proxy_info: ", proxy.proxy_info
# prints "Proxy Overlord"
print "proxy.proxy_name: ", proxy.proxy_name
# Raises AttributeError since proxy_name is only set on
# the proxy object
print "original.proxy_name: ", proxy.proxy_name
আপনার কাছে - মূলত - কোনও কাজের নয় __slots__
।
আপনি যখন প্রয়োজন মনে করেন সেই সময়ের জন্য __slots__
, আপনি আসলে লাইটওয়েট বা ফ্লাইওয়েট ডিজাইনের নিদর্শনগুলি ব্যবহার করতে চান । এগুলি হ'ল যখন আপনি আর পাইথন অবজেক্টগুলি আর ব্যবহার করতে চান না। পরিবর্তে, আপনি একটি অ্যারের, কাঠামো, বা নমির অ্যারের চারপাশে পাইথন অবজেক্টের মতো মোড়ক চাই।
class Flyweight(object):
def get(self, theData, index):
return theData[index]
def set(self, theData, index, value):
theData[index]= value
শ্রেণীর মতো মোড়কের কোনও বৈশিষ্ট্য নেই - এটি কেবল এমন পদ্ধতি সরবরাহ করে যা অন্তর্নিহিত ডেটাগুলিতে কাজ করে। পদ্ধতিগুলি ক্লাস পদ্ধতিতে হ্রাস করা যেতে পারে। প্রকৃতপক্ষে, এটি কেবলমাত্র ডেটা অন্তর্নিহিত অ্যারে অপারেটিং ফাংশন হ্রাস করা যেতে পারে।
__slots__
?
__slots__
মেমরি সংরক্ষণের জন্য উভয়ই অনুকূলিতকরণ কৌশল। __slots__
আপনার অনেকগুলি অবজেক্টের পাশাপাশি ফ্লাইওয়েট ডিজাইনের ধরণ থাকলে উপকারগুলি দেখায়। দুজনেই একই সমস্যা সমাধান করে।
__slots__
সত্যই উত্তরটি হয় এবং এভগেনি যেমন উল্লেখ করে, এটি একটি সাধারণ চিন্তাভাবনা হিসাবে যুক্ত করা যেতে পারে (যেমন আপনি প্রথমে যথার্থতার দিকে মনোনিবেশ করতে পারেন, এবং তারপরে পারফরম্যান্স যুক্ত করতে পারেন)।
মূল প্রশ্নটি ছিল কেবল ব্যবহারের ক্ষেত্রে মেমরির ক্ষেত্রে নয়। সুতরাং এখানে উল্লেখ করা উচিত যে বৃহত পরিমাণে অবজেক্টগুলি ইনস্ট্যান্ট করার সময় আপনি আরও ভাল পারফরম্যান্স পাবেন - আকর্ষণীয় উদাহরণস্বরূপ যখন বড় দলিলগুলিকে বস্তুগুলিতে বা একটি ডাটাবেস থেকে পার্স করার সময়।
এখানে মিলিয়ন এন্ট্রি সহ স্লট ব্যবহার করে এবং স্লট ছাড়াই অবজেক্ট ট্রি তৈরির তুলনা করা হয়েছে। রেফারেন্স হিসাবে গাছগুলির জন্য প্লেইন ডিক্টস ব্যবহার করার সময় কার্য সম্পাদন (ওএসএক্সে পাই 2.7.10):
********** RUN 1 **********
1.96036410332 <class 'css_tree_select.element.Element'>
3.02922606468 <class 'css_tree_select.element.ElementNoSlots'>
2.90828204155 dict
********** RUN 2 **********
1.77050495148 <class 'css_tree_select.element.Element'>
3.10655999184 <class 'css_tree_select.element.ElementNoSlots'>
2.84120798111 dict
********** RUN 3 **********
1.84069895744 <class 'css_tree_select.element.Element'>
3.21540498734 <class 'css_tree_select.element.ElementNoSlots'>
2.59615707397 dict
********** RUN 4 **********
1.75041103363 <class 'css_tree_select.element.Element'>
3.17366290092 <class 'css_tree_select.element.ElementNoSlots'>
2.70941114426 dict
পরীক্ষার ক্লাস (পরিচয়, স্লট থেকে সংযোজন):
class Element(object):
__slots__ = ['_typ', 'id', 'parent', 'childs']
def __init__(self, typ, id, parent=None):
self._typ = typ
self.id = id
self.childs = []
if parent:
self.parent = parent
parent.childs.append(self)
class ElementNoSlots(object): (same, w/o slots)
টেস্টকোড, ভার্বোজ মোড:
na, nb, nc = 100, 100, 100
for i in (1, 2, 3, 4):
print '*' * 10, 'RUN', i, '*' * 10
# tree with slot and no slot:
for cls in Element, ElementNoSlots:
t1 = time.time()
root = cls('root', 'root')
for i in xrange(na):
ela = cls(typ='a', id=i, parent=root)
for j in xrange(nb):
elb = cls(typ='b', id=(i, j), parent=ela)
for k in xrange(nc):
elc = cls(typ='c', id=(i, j, k), parent=elb)
to = time.time() - t1
print to, cls
del root
# ref: tree with dicts only:
t1 = time.time()
droot = {'childs': []}
for i in xrange(na):
ela = {'typ': 'a', id: i, 'childs': []}
droot['childs'].append(ela)
for j in xrange(nb):
elb = {'typ': 'b', id: (i, j), 'childs': []}
ela['childs'].append(elb)
for k in xrange(nc):
elc = {'typ': 'c', id: (i, j, k), 'childs': []}
elb['childs'].append(elc)
td = time.time() - t1
print td, 'dict'
del droot
class Child(BaseA, BaseB): __slots__ = ('a', 'b')
খালি-স্লট-পিতামাতার সাথে উদাহরণটি পাইনি। কেন এখানে একটিdictproxy
পরিবর্তে একটি উত্থাপন সৃষ্টিAttributeError
জন্যc
?