जवाबों:
पायथन में, इसका उद्देश्य क्या है
__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
जब आप इसे गलत पाते हैं तो पायथन को कोई आपत्ति नहीं है (यह शायद चाहिए), समस्याएं अन्यथा प्रकट नहीं हो सकती हैं, लेकिन आपकी वस्तुओं को उनकी तुलना में अधिक जगह लेनी चाहिए, अन्यथा नहीं। पायथन 3.8:
>>> 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'
सबसे बड़ी चेतावनी कई उत्तराधिकारियों के लिए है - कई "पैरेंट क्लास के साथ नॉनमीप्टी स्लॉट्स" को जोड़ा नहीं जा सकता है।
इस प्रतिबंध को समायोजित करने के लिए, सर्वोत्तम प्रथाओं का पालन करें: सभी लेकिन एक या सभी माता-पिता के अमूर्त कारक को निकाल दें जो क्रमशः उनके ठोस वर्ग और आपके नए ठोस वर्ग को सामूहिक रूप से विरासत में मिलेगा - अमूर्त (ओं) को खाली स्लॉट्स देना (जैसे अमूर्त वर्ग वर्गों में) मानक पुस्तकालय)।
एक उदाहरण के लिए नीचे दिए गए कई उत्तराधिकार पर अनुभाग देखें।
__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__
महत्वपूर्ण हो सकते हैं।
SQLAlchemy बहुत सारी मेमोरी सेविंग का श्रेय देता है __slots__
।
इसे सत्यापित करने के लिए, उबंटू लिनक्स पर पायथन 2.7 के एनाकोंडा वितरण का उपयोग करते हुए guppy.hpy
(उर्फ हीप) और sys.getsizeof
, बिना __slots__
घोषित किए एक वर्ग उदाहरण का आकार , और कुछ नहीं, 64 बाइट्स है। यही कारण है कि है नहीं शामिल __dict__
। फिर से आलसी मूल्यांकन के लिए पाइथन को धन्यवाद दें, __dict__
यह स्पष्ट रूप से अस्तित्व में नहीं है जब तक कि इसे संदर्भित नहीं किया जाता है, लेकिन डेटा के बिना कक्षाएं आमतौर पर बेकार होती हैं। जब अस्तित्व में कहा जाता है, तो __dict__
विशेषता अतिरिक्त 280 बाइट्स की एक न्यूनतम होती है।
इसके विपरीत, एक वर्ग उदाहरण के साथ __slots__
घोषित ()
(कोई डेटा नहीं) केवल 16 बाइट्स है, और 56 कुल बाइट्स एक आइटम के साथ स्लॉट्स में, 64 दो के साथ।
64 बिट पायथन के लिए, मैं पायथन 2.7 और 3.6 में बाइट्स में मेमोरी खपत का वर्णन करता हूं, __slots__
और __dict__
प्रत्येक बिंदु के लिए और (कोई स्लॉट निर्धारित नहीं) जहां 3.6 में तानाशाही बढ़ती है (0, 1, और 2 विशेषताओं को छोड़कर):
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 में क्लास के 64 बाइट्स के नाम स्थान में प्रति स्लॉट और पायथन 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__
, जबकि slotted वस्तुओं उपवर्गीकरण, बस जोड़ने के निर्माण '__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__
माता-पिता से , या यदि आपके पास माता-पिता का नियंत्रण है, तो उन्हें खाली स्लॉट दें, या अमूर्त को रिफ्लेक्टर दें:
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__'
स्लॉट्स में हम डायनामिक असाइनमेंट होने के साथ कुछ साइज़ बेनिफिट्स खो देते हैं और फिर भी उन नामों के लिए स्लॉट्स होते हैं जिनकी हम उम्मीद करते हैं।
जब आप किसी ऐसी वस्तु से विरासत में आते हैं, जिसे सुव्यवस्थित नहीं किया जाता है, तो आप उसी तरह के शब्दार्थ का उपयोग करते हैं जब आप __slots__
उस नाम का उपयोग करते हैं__slots__
स्लॉटेड मानों के लिए हैं, जबकि किसी अन्य मान को उदाहरण के लिए रखा जाता है __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
नॉनम खाली स्लॉट्स को ए और बी दे सकते थे, और उन्हें नीचे की घोषणा से छोड़ दिया - उन्हें छोड़ना गलत होगा):
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__
किसी अन्य वर्ग के साथ असाइनमेंट (जब तक कि स्लॉट लेआउट समान नहीं हों, तब तक उनके पास नहीं है (और आप उन्हें जोड़ नहीं सकते)। (मुझे यह सीखने में बहुत दिलचस्पी है कि यह कौन कर रहा है और क्यों।)आप बाकी __slots__
दस्तावेज़ीकरण (3.7 देव डॉक्स सबसे वर्तमान हैं) से और अधिक छेड़छाड़ करने में सक्षम हो सकते हैं , जिसे मैंने हाल ही में महत्वपूर्ण योगदान दिया है।
वर्तमान शीर्ष उत्तर पुरानी जानकारी का हवाला देते हैं और काफी हाथ से लहराते हैं और कुछ महत्वपूर्ण तरीकों से निशान को याद करते हैं।
__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
तर्क के साथ नवीनतम प्रोटोकॉल का चयन कर सकते हैं । पायथन 2.7 में यह होगा 2
(जिसे 2.3 में पेश किया गया था), और 3.6 में है 4
।
>>> pickle.loads(pickle.dumps(f, -1))
<__main__.Foo object at 0x1129C770>
पायथन 2.7 में:
>>> pickle.loads(pickle.dumps(f, 2))
<__main__.Foo object at 0x1129C770>
पायथन में 3.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
...
यह पायथन के इतिहास के अनुरूप है, पायथन 2.2 में यूनिफाइंग प्रकार और कक्षाओं से
यदि आप एक अंतर्निहित प्रकार को उप-वर्ग करते हैं, तो अतिरिक्त स्थान स्वचालित रूप से जोड़ने के लिए उदाहरणों में जोड़ा जाता है
__dict__
और__weakrefs__
। (__dict__
जब तक आप इसे उपयोग नहीं करते हैं, तब तक इसे आरंभिक नहीं किया जाता है, इसलिए आपको अपने द्वारा बनाए गए प्रत्येक उदाहरण के लिए खाली शब्दकोश द्वारा कब्जा किए गए स्थान के बारे में चिंता नहीं करनी चाहिए।) यदि आपको इस अतिरिक्त स्थान की आवश्यकता नहीं है, तो आप वाक्यांश "__slots__ = []
" जोड़ सकते हैं। आपकी कक्षा।
__slots__
। गंभीरता से! धन्यवाद!
__slots__
: एक साल वापस बारे में github.com/python/cpython/pull/1819/files
कोटिंग जैकब हॉलन :
__slots__
वस्तुओं में स्थान बचाने के लिए इसका उचित उपयोग है। एक गतिशील तानाशाही होने के बजाय जो किसी भी समय वस्तुओं को जोड़ने की अनुमति देती है, एक स्थिर संरचना है जो निर्माण के बाद परिवर्धन की अनुमति नहीं देती है। [यह उपयोग__slots__
हर वस्तु के लिए एक तानाशाही के उपरि को समाप्त करता है।] जबकि यह कभी-कभी एक उपयोगी अनुकूलन होता है, यह पूरी तरह से अनावश्यक होगा यदि पायथन दुभाषिया इतना गतिशील था कि इसे केवल तब ही तानाशाह की आवश्यकता होगी जब वास्तव में परिवर्धन थे। वस्तु।दुर्भाग्य से स्लॉट्स के लिए एक साइड इफेक्ट है। वे उन वस्तुओं के व्यवहार को बदल देते हैं जिनके पास स्लॉट्स होते हैं जिन्हें नियंत्रण शैतान और स्थिर टाइपिंग वेनिज़ द्वारा दुरुपयोग किया जा सकता है। यह बुरा है, क्योंकि नियंत्रण शैतान को मेटाक्लासेस को गाली देनी चाहिए और स्थैतिक टाइपिंग वेनियां डेकोरेटर्स को गाली देनी चाहिए, क्योंकि पायथन में, कुछ करने का केवल एक स्पष्ट तरीका होना चाहिए।
CPython को स्मार्ट बनाना, बिना अंतरिक्ष को बचाने के
__slots__
लिए एक बड़ा उपक्रम है, जो संभवतः P3k (अभी तक) के बदलावों की सूची में नहीं है।
__slots__
स्थैतिक टाइपिंग के समान मुद्दों को संबोधित नहीं करता है। उदाहरण के लिए, C ++ में, यह सदस्य चर की घोषणा को प्रतिबंधित नहीं किया जा रहा है, यह उस चर के लिए एक अनपेक्षित प्रकार (और संकलित लागू) का असाइनमेंट है। मैं उपयोग नहीं कर रहा हूँ __slots__
, बस बातचीत में दिलचस्पी है। धन्यवाद!
आप उपयोग करना चाहते हैं __slots__
यदि आप एक ही वर्ग की वस्तुओं के बहुत (सैकड़ों, हजारों) पलटने जा रहे हैं। __slots__
केवल मेमोरी ऑप्टिमाइज़ेशन टूल के रूप में मौजूद है।
यह __slots__
विशेषता निर्माण के लिए विवश करने के लिए अत्यधिक हतोत्साहित है ।
__slots__
डिफ़ॉल्ट (सबसे पुराना) अचार प्रोटोकॉल के साथ काम करने वाली वस्तुओं को अचार बनाना नहीं होगा; बाद के संस्करण को निर्दिष्ट करना आवश्यक है।
अजगर की कुछ अन्य आत्मनिरीक्षण विशेषताएं भी प्रतिकूल रूप से प्रभावित हो सकती हैं।
प्रत्येक अजगर वस्तु में एक __dict__
गुण है जो अन्य सभी विशेषताओं से युक्त एक शब्दकोश है। जैसे जब आप self.attr
अजगर टाइप करते हैं तो वास्तव में कर रहे होते हैं self.__dict__['attr']
। जैसा कि आप स्टोर करने के लिए एक शब्दकोश का उपयोग करने की कल्पना कर सकते हैं विशेषता इसे एक्सेस करने के लिए कुछ अतिरिक्त स्थान और समय लेती है।
हालाँकि, जब आप उपयोग करते हैं, तो __slots__
उस वर्ग के लिए बनाई गई किसी भी वस्तु की __dict__
विशेषता नहीं होगी । इसके बजाय, सभी विशेषता पहुंच सीधे पॉइंटर्स के माध्यम से की जाती है।
इसलिए यदि आप पूर्ण विकसित वर्ग के बजाय C शैली संरचना चाहते हैं, तो आप __slots__
वस्तुओं के आकार और विशेषता पहुंच समय को कम करने के लिए उपयोग कर सकते हैं। एक अच्छा उदाहरण एक बिंदु वर्ग है जिसमें एक्स और वाई के गुण हैं। यदि आपके पास बहुत सारे बिंदु हैं, तो आप __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-गुना कम कर सकते हैं , यदि और जब आवश्यक हो।
फ़ंक्शन कॉल करते समय "नामित विधि प्रेषण" को समाप्त करने के लिए लाइब्रेरी कॉल के लिए स्लॉट बहुत उपयोगी होते हैं। यह SWIG प्रलेखन में उल्लेख किया गया है । उच्च निष्पादन पुस्तकालयों के लिए जो स्लॉट्स का उपयोग करके आमतौर पर तथाकथित फ़ंक्शन के लिए फ़ंक्शन ओवरहेड को कम करना चाहते हैं, बहुत तेज है।
अब यह सीधे ओपी प्रश्न से संबंधित नहीं हो सकता है। यह ऑब्जेक्ट पर स्लॉट्स सिंटैक्स का उपयोग करने की तुलना में एक्सटेंशन बनाने से अधिक संबंधित है । लेकिन यह स्लॉट के उपयोग के लिए तस्वीर को पूरा करने में मदद करता है और उनके पीछे के कुछ तर्क।
एक वर्ग उदाहरण की विशेषता में 3 गुण होते हैं: उदाहरण, विशेषता का नाम और विशेषता का मान।
में नियमित विशेषता का उपयोग , उदाहरण के एक शब्दकोश के रूप में कार्य करता है और के रूप में है कि शब्दकोश में कुंजी मान को देख विशेषता का नाम कार्य करता है।
उदाहरण (विशेषता) -> मान
में __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}
यदि आप ऊपर दिए गए उदाहरण को देखते हैं, तो आप देख सकते हैं कि obj1 और obj2 की अपनी x और y विशेषताएँ हैं और अजगर ने dict
प्रत्येक ऑब्जेक्ट ( obj1 और obj2 ) के लिए एक विशेषता भी बनाई है ।
मान लीजिए अगर मेरे क्लास टेस्ट में ऐसी हजारों वस्तुएं हैं? 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__
ProxyTypes पैकेज से ऑब्जेक्ट प्रॉक्सी के लिए विशेषताओं को जोड़ना है, जो पहले PEAK प्रोजेक्ट का हिस्सा था। यह ObjectWrapper
आपको किसी अन्य ऑब्जेक्ट को प्रॉक्सी करने की अनुमति देता है, लेकिन अनुमानित ऑब्जेक्ट के साथ सभी इंटरैक्शन को इंटरसेप्ट करता है। यह आमतौर पर बहुत अधिक उपयोग नहीं किया जाता है (और पायथन 3 समर्थन नहीं है), लेकिन हमने इसका उपयोग एक थ्रेड-आधारित पर एक एसिंक्स कार्यान्वयन के चारों ओर एक थ्रेड-सुरक्षित अवरोधक आवरण को लागू करने के लिए किया है, जो थ्रेड-सेफ का उपयोग करते हुए, ioloop के माध्यम से समीपस्थ वस्तु तक सभी पहुंच को बाउंस करता है। 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__
वास्तव में इसका उत्तर होता है, और जैसा कि एवगेनी बताते हैं, इसे एक सरल बाद में जोड़ा जा सकता है (जैसे आप पहले शुद्धता पर ध्यान केंद्रित कर सकते हैं, और फिर प्रदर्शन जोड़ सकते हैं)।
मूल प्रश्न केवल स्मृति के बारे में नहीं सामान्य उपयोग के मामलों के बारे में था। तो यहां यह उल्लेख किया जाना चाहिए कि जब आप बड़ी मात्रा में ऑब्जेक्ट्स को डेटाबेस से या किसी डेटाबेस से पार्स कर रहे हों तो बड़ी मात्रा में ऑब्जेक्ट्स को इंस्टेंट करते समय आपको बेहतर प्रदर्शन भी मिलता है ।
यहां एक लाख प्रविष्टियों के साथ ऑब्जेक्ट ट्री बनाने, स्लॉट्स और बिना स्लॉट्स का उपयोग करने की तुलना है। एक संदर्भ के रूप में भी प्रदर्शन जब पेड़ों के लिए सादे dicts (OSX पर Py2.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
?