పైథాన్ __slots__తో మెమరీ వినియోగాన్ని తగ్గించండి, అట్రిబ్యూట్ యాక్సెస్ వేగాన్ని పెంచండి. బెంచ్మార్క్లు, రాజీలు, ఉత్తమ పద్ధతులతో కూడిన సమగ్ర గైడ్.
పైథాన్ యొక్క __slots__: మెమరీ ఆప్టిమైజేషన్ మరియు అట్రిబ్యూట్ స్పీడ్పై లోతైన విశ్లేషణ
సాఫ్ట్వేర్ డెవలప్మెంట్ ప్రపంచంలో, పనితీరు అత్యంత ముఖ్యం. పైథాన్ డెవలపర్ల విషయానికి వస్తే, ఇది తరచుగా భాష యొక్క అద్భుతమైన సౌలభ్యం మరియు వనరుల సామర్థ్యం అవసరం మధ్య సున్నితమైన సమతుల్యతను కలిగి ఉంటుంది. డేటా-ఇంటెన్సివ్ అప్లికేషన్లలో ముఖ్యంగా సాధారణ సవాళ్లలో ఒకటి మెమరీ వినియోగాన్ని నిర్వహించడం. మీరు మిలియన్లు లేదా బిలియన్ల చిన్న వస్తువులను సృష్టిస్తున్నప్పుడు, ప్రతి బైట్ ముఖ్యమైనది.
ఇక్కడే పైథాన్లోని తక్కువ-తెలిసిన కానీ శక్తివంతమైన ఫీచర్ అమలులోకి వస్తుంది: __slots__
. ఇది తరచుగా మెమరీ ఆప్టిమైజేషన్ కోసం ఒక మేజిక్ బుల్లెట్గా ప్రశంసించబడుతుంది, కానీ దాని నిజమైన స్వభావం మరింత సూక్ష్మంగా ఉంటుంది. ఇది కేవలం మెమరీని ఆదా చేయడం గురించేనా? ఇది నిజంగా మీ కోడ్ను వేగవంతం చేస్తుందా? మరియు దీనిని ఉపయోగించడం వల్ల దాగి ఉన్న ఖర్చులు ఏమిటి?
ఈ సమగ్ర మార్గదర్శిని పైథాన్ యొక్క __slots__
లోకి లోతైన విశ్లేషణను అందిస్తుంది. ప్రామాణిక పైథాన్ వస్తువులు లోపల ఎలా పని చేస్తాయో మేము విశ్లేషిస్తాము, మెమరీ మరియు వేగంపై __slots__
యొక్క వాస్తవ-ప్రపంచ ప్రభావాన్ని బెంచ్మార్క్ చేస్తాము, దాని ఆశ్చర్యకరమైన సంక్లిష్టతలు మరియు రాజీలను అన్వేషిస్తాము మరియు ఈ శక్తివంతమైన ఆప్టిమైజేషన్ సాధనాన్ని ఎప్పుడు—మరియు ఎప్పుడు కాదు—ఉపయోగించాలో నిర్ణయించడానికి స్పష్టమైన ఫ్రేమ్వర్క్ను అందిస్తాము.
డిఫాల్ట్: పైథాన్ ఆబ్జెక్ట్లు `__dict__`తో అట్రిబ్యూట్లను ఎలా నిల్వ చేస్తాయి
__slots__
ఏమి చేస్తుందో మనం అభినందించడానికి ముందు, అది దేనిని భర్తీ చేస్తుందో మనం ముందుగా అర్థం చేసుకోవాలి. డిఫాల్ట్గా, పైథాన్లో కస్టమ్ క్లాస్ యొక్క ప్రతి ఇన్స్టాన్స్కి __dict__
అనే ప్రత్యేక అట్రిబ్యూట్ ఉంటుంది. ఇది అక్షరాలా, ఇన్స్టాన్స్ యొక్క అన్ని అట్రిబ్యూట్లను నిల్వ చేసే నిఘంటువు.
ఒక సాధారణ ఉదాహరణ చూద్దాం: 2D పాయింట్ను సూచించడానికి ఒక క్లాస్.
import sys
class Point2D:
def __init__(self, x, y):
self.x = x
self.y = y
# Create an instance
p1 = Point2D(10, 20)
# Attributes are stored in __dict__
print(p1.__dict__) # Output: {'x': 10, 'y': 20}
# Let's check the size of the __dict__ itself
print(f"Size of the Point2D instance's __dict__: {sys.getsizeof(p1.__dict__)} bytes")
మీ పైథాన్ వెర్షన్ మరియు సిస్టమ్ ఆర్కిటెక్చర్ ఆధారంగా అవుట్పుట్ కొద్దిగా మారవచ్చు (ఉదాహరణకు, చిన్న డిక్షనరీ కోసం పైథాన్ 3.10+లో 64 బైట్లు), కానీ ముఖ్యమైన విషయం ఏమిటంటే ఈ డిక్షనరీకి దాని స్వంత మెమరీ ఫుట్ప్రింట్ ఉంటుంది, ఇది ఇన్స్టాన్స్ ఆబ్జెక్ట్ నుండి మరియు అది కలిగి ఉన్న విలువల నుండి వేరుగా ఉంటుంది.
సౌలభ్యం యొక్క శక్తి మరియు విలువ
ఈ __dict__
విధానం పైథాన్ యొక్క డైనమిజంకు మూలస్తంభం. ఇది మీకు ఎప్పుడైనా ఒక ఇన్స్టాన్స్కు కొత్త అట్రిబ్యూట్లను జోడించడానికి అనుమతిస్తుంది, ఈ పద్ధతిని తరచుగా "మంకీ-ప్యాచ్ చేయడం" అని పిలుస్తారు:
# Add a new attribute on the fly
p1.z = 30
print(p1.__dict__) # Output: {'x': 10, 'y': 20, 'z': 30}
ఈ సౌలభ్యం వేగవంతమైన అభివృద్ధికి మరియు కొన్ని ప్రోగ్రామింగ్ పద్ధతులకు అద్భుతమైనది. అయితే, దీనికి ఒక ఖర్చు ఉంది: మెమరీ ఓవర్హెడ్.
పైథాన్లోని డిక్షనరీలను బాగా ఆప్టిమైజ్ చేసినప్పటికీ, అవి సరళమైన డేటా స్ట్రక్చర్ల కంటే అంతర్గతంగా సంక్లిష్టంగా ఉంటాయి. అవి వేగవంతమైన కీ లుకప్లను అందించడానికి ఒక హాష్ టేబుల్ను నిర్వహించాలి, దీనికి సంభావ్య హాష్ ఘర్షణలను నిర్వహించడానికి మరియు సమర్థవంతమైన రీసైజింగ్ను అనుమతించడానికి అదనపు మెమరీ అవసరం. మీరు మిలియన్ల కొద్దీ Point2D
ఇన్స్టాన్స్లను సృష్టించినప్పుడు, ప్రతి ఒక్కటి దాని స్వంత __dict__
ని కలిగి ఉంటుంది, ఈ మెమరీ ఓవర్హెడ్ త్వరగా పేరుకుపోతుంది.
10 మిలియన్ల వర్టెక్స్లతో 3D మోడల్ను ప్రాసెస్ చేసే అప్లికేషన్ను ఊహించండి. ప్రతి వర్టెక్స్ ఆబ్జెక్ట్కు 64 బైట్ల __dict__
ఉంటే, అది 640 మెగాబైట్ల మెమరీని డిక్షనరీల ద్వారా మాత్రమే వినియోగించుకుంటుంది, అవి నిల్వ చేసే వాస్తవ పూర్ణాంకం లేదా ఫ్లోట్ విలువలను లెక్కించకుండానే! ఇది __slots__
పరిష్కరించడానికి రూపొందించబడిన సమస్య.
`__slots__`ని పరిచయం చేస్తున్నాము: మెమరీ-పొదుపు ప్రత్యామ్నాయం
__slots__
అనేది ఒక క్లాస్ వేరియబుల్, ఇది ఒక ఇన్స్టాన్స్కు ఉండే అట్రిబ్యూట్లను స్పష్టంగా ప్రకటించడానికి మిమ్మల్ని అనుమతిస్తుంది. __slots__
ని నిర్వచించడం ద్వారా, మీరు పైథాన్కు తప్పనిసరిగా ఇలా చెబుతున్నారు: "ఈ క్లాస్ యొక్క ఇన్స్టాన్స్లు ఈ నిర్దిష్ట అట్రిబ్యూట్లను మాత్రమే కలిగి ఉంటాయి. వాటి కోసం __dict__
ని సృష్టించాల్సిన అవసరం లేదు."
డిక్షనరీకి బదులుగా, పైథాన్ ఇన్స్టాన్స్ కోసం మెమరీలో స్థిరమైన స్థలాన్ని కేటాయిస్తుంది, ప్రకటించిన అట్రిబ్యూట్ల విలువలకి పాయింటర్లను నిల్వ చేయడానికి సరిపోతుంది, C స్ట్రక్ట్ లేదా టూపుల్ లాగా.
మన Point2D
క్లాస్ను __slots__
ని ఉపయోగించడానికి రీఫ్యాక్టర్ చేద్దాం.
class SlottedPoint2D:
# Declare the instance attributes
# It can be a tuple (most common), list, or any iterable of strings.
__slots__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y
పైపైన చూస్తే, ఇది దాదాపు ఒకేలా కనిపిస్తుంది. కానీ లోపల, ప్రతిదీ మారిపోయింది. ద __dict__
లేదు.
p_slotted = SlottedPoint2D(10, 20)
# Trying to access __dict__ will raise an error
try:
print(p_slotted.__dict__)
except AttributeError as e:
print(e) # Output: 'SlottedPoint2D' object has no attribute '__dict__'
మెమరీ పొదుపులను బెంచ్మార్క్ చేయడం
మెమరీ వినియోగాన్ని పోల్చినప్పుడు నిజమైన "వావ్" క్షణం వస్తుంది. దీన్ని ఖచ్చితంగా చేయడానికి, ఆబ్జెక్ట్ పరిమాణాన్ని ఎలా కొలుస్తారో మనం అర్థం చేసుకోవాలి. sys.getsizeof()
ఒక ఆబ్జెక్ట్ యొక్క బేస్ పరిమాణాన్ని నివేదిస్తుంది, కానీ అది సూచించే __dict__
వంటి వాటి పరిమాణాన్ని కాదు.
import sys
# --- Regular Class ---
class Point2D:
def __init__(self, x, y):
self.x = x
self.y = y
# --- Slotted Class ---
class SlottedPoint2D:
__slots__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y
# Create one instance of each to compare
p_normal = Point2D(1, 2)
p_slotted = SlottedPoint2D(1, 2)
# The size of the slotted instance is much smaller
# It's typically the base object size plus a pointer for each slot.
size_slotted = sys.getsizeof(p_slotted)
# The size of the normal instance includes its base size and a pointer to its __dict__.
# The total size is the instance size + the __dict__ size.
size_normal = sys.getsizeof(p_normal) + sys.getsizeof(p_normal.__dict__)
print(f"Size of a single SlottedPoint2D instance: {size_slotted} bytes")
print(f"Total memory footprint of a single Point2D instance: {size_normal} bytes")
# Now let's see the impact at scale
NUM_INSTANCES = 1_000_000
# In a real application, you would use a tool like memory_profiler
# to measure the total memory usage of the process.
# We can estimate the savings based on our single-instance calculation.
size_diff_per_instance = size_normal - size_slotted
total_memory_saved = size_diff_per_instance * NUM_INSTANCES
print(f"\nCreating {NUM_INSTANCES:,} instances...")
print(f"Memory saved per instance by using __slots__: {size_diff_per_instance} bytes")
print(f"Estimated total memory saved: {total_memory_saved / (1024*1024):.2f} MB")
సాధారణ 64-బిట్ సిస్టమ్లో, మీరు ప్రతి ఇన్స్టాన్స్కు 40-50% మెమరీ పొదుపును ఆశించవచ్చు. ఒక సాధారణ వస్తువు దాని బేస్ కోసం 16 బైట్లు + 8 బైట్లు __dict__
పాయింటర్ కోసం + ఖాళీ __dict__
కోసం 64 బైట్లు, మొత్తం 88 బైట్లు పట్టవచ్చు. రెండు అట్రిబ్యూట్లతో కూడిన స్లాటెడ్ ఆబ్జెక్ట్ 32 బైట్లు మాత్రమే పట్టవచ్చు. ప్రతి ఇన్స్టాన్స్కు ~56 బైట్ల వ్యత్యాసం ఒక మిలియన్ ఇన్స్టాన్స్లకు 56 MB ఆదా అవుతుంది. ఇది మైక్రో-ఆప్టిమైజేషన్ కాదు; ఇది అసాధ్యమైన అప్లికేషన్ను సాధ్యం చేసే ప్రాథమిక మార్పు.
రెండవ వాగ్దానం: వేగవంతమైన అట్రిబ్యూట్ యాక్సెస్
మెమరీ పొదుపులతో పాటు, __slots__
పనితీరును మెరుగుపరుస్తుందని కూడా ప్రశంసించబడింది. సిద్ధాంతం సరైనది: స్థిరమైన మెమరీ ఆఫ్సెట్ (అరే ఇండెక్స్ లాగా) నుండి విలువను యాక్సెస్ చేయడం డిక్షనరీలో హాష్ లుకప్ను నిర్వహించడం కంటే వేగంగా ఉంటుంది.
__dict__
యాక్సెస్:obj.x
అంటే కీ'x'
కోసం డిక్షనరీ లుకప్.__slots__
యాక్సెస్:obj.x
అంటే ఒక నిర్దిష్ట స్లాట్కు ప్రత్యక్ష మెమరీ యాక్సెస్.
కానీ ఆచరణలో ఎంత వేగంగా ఉంటుంది? తెలుసుకోవడానికి పైథాన్ యొక్క అంతర్నిర్మిత timeit
మాడ్యూల్ను ఉపయోగిద్దాం.
import timeit
# Setup code to be run once before timing
SETUP_CODE = """
class Point2D:
def __init__(self, x, y):
self.x = x
self.y = y
class SlottedPoint2D:
__slots__ = 'x', 'y'
def __init__(self, x, y):
self.x = x
self.y = y
p_normal = Point2D(1, 2)
p_slotted = SlottedPoint2D(1, 2)
"""
# Test attribute reading
read_normal = timeit.timeit("p_normal.x", setup=SETUP_CODE, number=10_000_000)
read_slotted = timeit.timeit("p_slotted.x", setup=SETUP_CODE, number=10_000_000)
print("--- Attribute Reading ---")
print(f"Time for __dict__ access: {read_normal:.4f} seconds")
print(f"Time for __slots__ access: {read_slotted:.4f} seconds")
speedup = (read_normal - read_slotted) / read_normal * 100
print(f"Speedup: <strong>{speedup:.2f}%</strong>")
print("\n--- Attribute Writing ---")
# Test attribute writing
write_normal = timeit.timeit("p_normal.x = 3", setup=SETUP_CODE, number=10_000_000)
write_slotted = timeit.timeit("p_slotted.x = 3", setup=SETUP_CODE, number=10_000_000)
print(f"Time for __dict__ access: {write_normal:.4f} seconds")
print(f"Time for __slots__ access: {write_slotted:.4f} seconds")
speedup = (write_normal - write_slotted) / write_normal * 100
print(f"Speedup: <strong>{speedup:.2f}%</strong>")
ఫలితాలు __slots__
నిజంగా వేగంగా ఉంటుందని చూపుతాయి, కానీ మెరుగుదల సాధారణంగా 10-20% పరిధిలో ఉంటుంది. ఇది అంత తక్కువ కానప్పటికీ, మెమరీ పొదుపుల కంటే చాలా తక్కువ నాటకీయంగా ఉంటుంది.
ముఖ్యమైన అంశం: __slots__
ని ప్రాథమికంగా మెమరీ ఆప్టిమైజేషన్ కోసం ఉపయోగించండి. వేగ మెరుగుదలను స్వాగతించదగిన, కానీ ద్వితీయ, బోనస్గా పరిగణించండి. పనితీరు లాభం అత్యధికంగా గణన-ఇంటెన్సివ్ అల్గారిథమ్లలోని టైట్ లూప్లలో, అట్రిబ్యూట్ యాక్సెస్ మిలియన్ల సార్లు జరిగినప్పుడు సంబంధితంగా ఉంటుంది.
రాజీలు మరియు "సాదించలేనివి": `__slots__`తో మీరు కోల్పోయేవి
__slots__
అనేది ఉచితంగా వచ్చే ప్రయోజనం కాదు. పనితీరు లాభాలు సౌలభ్యం కోల్పోవడం వల్ల వస్తాయి మరియు కొన్ని సంక్లిష్టతలను పరిచయం చేస్తాయి, ముఖ్యంగా ఇన్హెరిటెన్స్ విషయానికి వస్తే. ఈ రాజీలను అర్థం చేసుకోవడం __slots__
ని సమర్థవంతంగా ఉపయోగించడానికి చాలా ముఖ్యం.
1. డైనమిక్ అట్రిబ్యూట్లను కోల్పోవడం
ఇది అత్యంత ముఖ్యమైన పరిణామం. అట్రిబ్యూట్లను ముందుగానే నిర్వచించడం ద్వారా, మీరు రన్టైమ్లో కొత్త వాటిని జోడించే సామర్థ్యాన్ని కోల్పోతారు.
p_slotted = SlottedPoint2D(10, 20)
# This works fine
p_slotted.x = 100
# This will fail
try:
p_slotted.z = 30 # 'z' was not in __slots__
except AttributeError as e:
print(e) # Output: 'SlottedPoint2D' object has no attribute 'z'
ఈ ప్రవర్తన ఒక బగ్ కాదు, ఒక ఫీచర్ కావచ్చు. ఇది కఠినమైన వస్తువు నమూనాను అమలు చేస్తుంది, ప్రమాదవశాత్తు అట్రిబ్యూట్ సృష్టిని నిరోధిస్తుంది మరియు క్లాస్ యొక్క "ఆకారాన్ని" మరింత ఊహాజనితంగా చేస్తుంది. అయితే, మీ డిజైన్ డైనమిక్ అట్రిబ్యూట్ కేటాయింపుపై ఆధారపడి ఉంటే, __slots__
పనికిరాదు.
2. `__dict__` మరియు `__weakref__` లేకపోవడం
మనం చూసినట్లుగా, __slots__
__dict__
సృష్టిని నిరోధిస్తుంది. మీరు __dict__
ద్వారా ఇంట్రోస్పెక్షన్ పై ఆధారపడే లైబ్రరీలు లేదా సాధనాలతో పని చేయాల్సి వస్తే ఇది సమస్య కావచ్చు.
అదేవిధంగా, __slots__
__weakref__
స్వయంచాలక సృష్టిని కూడా నిరోధిస్తుంది, ఇది ఒక వస్తువు బలహీనంగా సూచించబడటానికి అవసరమైన లక్షణం. బలహీనమైన రిఫరెన్స్లు ఒక వస్తువును గార్బేజ్ కలెక్ట్ చేయకుండా నిరోధించకుండా ట్రాక్ చేయడానికి ఉపయోగించే ఒక అధునాతన మెమరీ నిర్వహణ సాధనం.
పరిష్కారం: మీకు అవసరమైతే మీరు '__dict__'
మరియు '__weakref__'
ని మీ __slots__
నిర్వచనంలో స్పష్టంగా చేర్చవచ్చు.
class HybridSlottedPoint:
# We get memory savings for x and y, but still have __dict__ and __weakref__
__slots__ = ('x', 'y', '__dict__', '__weakref__')
def __init__(self, x, y):
self.x = x
self.y = y
p_hybrid = HybridSlottedPoint(5, 10)
p_hybrid.z = 20 # This works now, because __dict__ is present!
print(p_hybrid.__dict__) # Output: {'z': 20}
import weakref
w_ref = weakref.ref(p_hybrid) # This also works now
print(w_ref)
'__dict__'
ని జోడించడం మీకు హైబ్రిడ్ మోడల్ను ఇస్తుంది. స్లాటెడ్ అట్రిబ్యూట్లు (x
, y
) ఇప్పటికీ సమర్థవంతంగా నిర్వహించబడతాయి, అయితే ఇతర అట్రిబ్యూట్లు __dict__
లో ఉంచబడతాయి. ఇది మెమరీ పొదుపులలో కొంత భాగాన్ని నిరాకరిస్తుంది కానీ అత్యంత సాధారణ అట్రిబ్యూట్లను ఆప్టిమైజ్ చేస్తూ సౌలభ్యాన్ని నిలుపుకోవడానికి ఉపయోగకరమైన రాజీ కావచ్చు.
3. ఇన్హెరిటెన్స్ యొక్క సంక్లిష్టతలు
ఇక్కడే __slots__
గందరగోళంగా మారవచ్చు. పేరెంట్ మరియు చైల్డ్ క్లాస్లు ఎలా నిర్వచించబడతాయో దానిపై ఆధారపడి దాని ప్రవర్తన మారుతుంది.
సింగిల్ ఇన్హెరిటెన్స్
-
ఒక పేరెంట్ క్లాస్కు
__slots__
ఉండి, చైల్డ్ క్లాస్కు లేకపోతే: చైల్డ్ క్లాస్ పేరెంట్ యొక్క అట్రిబ్యూట్ల కోసం స్లాటెడ్ ప్రవర్తనను వారసత్వంగా పొందుతుంది, కానీ దాని స్వంత__dict__
కూడా ఉంటుంది. దీని అర్థం చైల్డ్ క్లాస్ యొక్క ఇన్స్టాన్స్లు పేరెంట్ యొక్క ఇన్స్టాన్స్ల కంటే పెద్దవిగా ఉంటాయి.class SlottedBase: __slots__ = ('a',) class DictChild(SlottedBase): # No __slots__ defined here def __init__(self): self.a = 1 self.b = 2 # 'b' will be stored in __dict__ c = DictChild() print(f"Child has __dict__: {hasattr(c, '__dict__')}") # Output: True print(c.__dict__) # Output: {'b': 2}
-
పేరెంట్ మరియు చైల్డ్ క్లాస్లు రెండూ
__slots__
ని నిర్వచిస్తే: చైల్డ్ క్లాస్కు__dict__
ఉండదు. దాని ప్రభావవంతమైన__slots__
దాని స్వంత__slots__
మరియు దాని పేరెంట్ యొక్క__slots__
కలయిక అవుతుంది.class SlottedBase: __slots__ = ('a',) class SlottedChild(SlottedBase): __slots__ = ('b',) # Effective slots are ('a', 'b') def __init__(self): self.a = 1 self.b = 2 sc = SlottedChild() print(f"Child has __dict__: {hasattr(sc, '__dict__')}") # Output: False try: sc.c = 3 # Raises AttributeError except AttributeError as e: print(e)
__slots__
లో చైల్డ్ యొక్క__slots__
లో కూడా జాబితా చేయబడిన అట్రిబ్యూట్ ఉంటే, అది అనవసరం కానీ సాధారణంగా హానికరం కాదు.
మల్టిపుల్ ఇన్హెరిటెన్స్
__slots__
తో మల్టిపుల్ ఇన్హెరిటెన్స్ ఒక గని క్షేత్రం. నియమాలు కఠినంగా ఉంటాయి మరియు ఊహించని లోపాలకు దారితీయవచ్చు.
-
ప్రధాన నియమం: ఒక చైల్డ్ క్లాస్
__slots__
ని సమర్థవంతంగా ఉపయోగించాలంటే (అంటే,__dict__
లేకుండా), దాని పేరెంట్ క్లాస్ల అన్నింటికీ__slots__
కూడా ఉండాలి. ఒక పేరెంట్ క్లాస్కు కూడా__slots__
లేకపోతే (మరియు__dict__
ఉంటే), చైల్డ్ క్లాస్కు కూడా__dict__
ఉంటుంది. -
`TypeError` ట్రాప్: ఖాళీగా లేని
__slots__
ఉన్న బహుళ పేరెంట్ క్లాస్ల నుండి ఒక చైల్డ్ క్లాస్ వారసత్వంగా పొందలేదు.class SlotParentA: __slots__ = ('x',) class SlotParentB: __slots__ = ('y',) try: class ProblemChild(SlotParentA, SlotParentB): pass except TypeError as e: print(e) # Output: multiple bases have instance lay-out conflict
తీర్పు: `__slots__`ని ఎప్పుడు ఉపయోగించాలి మరియు ఎప్పుడు ఉపయోగించకూడదు
ప్రయోజనాలు మరియు నష్టాలపై స్పష్టమైన అవగాహనతో, మనం ఒక ఆచరణాత్మక నిర్ణయం తీసుకునే ఫ్రేమ్వర్క్ను స్థాపించవచ్చు.
గ్రీన్ ఫ్లాగ్స్: ఈ సందర్భాలలో `__slots__`ని ఉపయోగించండి...
- మీరు భారీ సంఖ్యలో ఇన్స్టాన్స్లను సృష్టిస్తున్నప్పుడు. ఇది ప్రాథమిక వినియోగ సందర్భం. మీరు మిలియన్ల కొద్దీ వస్తువులతో వ్యవహరిస్తున్నట్లయితే, మెమరీ పొదుపులు ఒక అప్లికేషన్ నడవడానికి మరియు క్రాష్ అవ్వడానికి మధ్య తేడాను కలిగి ఉంటాయి.
-
వస్తువు యొక్క లక్షణాలు స్థిరంగా మరియు ముందుగానే తెలిసినప్పుడు.
__slots__
డేటా స్ట్రక్చర్లు, రికార్డులు లేదా "ఆకారం" మారకుండా ఉండే సాధారణ డేటా వస్తువులకు సరైనది. - మీరు మెమరీ-పరిమిత వాతావరణంలో ఉన్నప్పుడు. ఇందులో IoT పరికరాలు, మొబైల్ అప్లికేషన్లు లేదా ప్రతి మెగాబైట్ విలువైన అధిక సాంద్రత గల సర్వర్లు ఉంటాయి.
-
మీరు పనితీరు అడ్డంకిని ఆప్టిమైజ్ చేస్తున్నప్పుడు. టైట్ లూప్లో అట్రిబ్యూట్ యాక్సెస్ గణనీయమైన మందగింపును చూపుతుందని ప్రొఫైలింగ్ చూపిస్తే,
__slots__
నుండి లభించే సాధారణ వేగ వృద్ధి విలువైనది కావచ్చు.
సాధారణ ఉదాహరణలు:
- పెద్ద గ్రాఫ్ లేదా ట్రీ స్ట్రక్చర్లో నోడ్లు.
- భౌతిక అనుకరణలో కణాలు.
- పెద్ద డేటాబేస్ ప్రశ్న నుండి వరుసలను సూచించే వస్తువులు.
- అధిక-త్రూపుట్ సిస్టమ్లో ఈవెంట్ లేదా సందేశ వస్తువులు.
రెడ్ ఫ్లాగ్స్: ఈ సందర్భాలలో `__slots__`ని నివారించండి...
-
సౌలభ్యం కీలకం అయినప్పుడు. మీ క్లాస్ సాధారణ ప్రయోజనాల కోసం రూపొందించబడినట్లయితే లేదా మీరు డైనమిక్గా అట్రిబ్యూట్లను జోడించడం (మంకీ-ప్యాచ్ చేయడం)పై ఆధారపడితే, డిఫాల్ట్
__dict__
తో కొనసాగండి. -
మీ క్లాస్ ఇతరులు సబ్క్లాస్ చేయదగిన పబ్లిక్ APIలో భాగమైనప్పుడు. బేస్ క్లాస్పై
__slots__
ని విధించడం అన్ని చైల్డ్ క్లాస్లపై పరిమితులను బలవంతం చేస్తుంది, ఇది మీ వినియోగదారులకు అవాంఛిత ఆశ్చర్యం కావచ్చు. -
మీరు సరిపడా ఇన్స్టాన్స్లను సృష్టించనప్పుడు. మీకు కొన్ని వందలు లేదా వేల ఇన్స్టాన్స్లు మాత్రమే ఉంటే, మెమరీ పొదుపులు చాలా తక్కువగా ఉంటాయి. ఇక్కడ
__slots__
ని వర్తింపజేయడం అనేది అసలు ప్రయోజనం లేకుండా సంక్లిష్టతను జోడించే అకాల ఆప్టిమైజేషన్. -
మీరు సంక్లిష్టమైన బహుళ వారసత్వ సోపానక్రమాలను ఎదుర్కొంటున్నప్పుడు.
TypeError
ఆంక్షలు ఈ సందర్భాలలో__slots__
ను అది విలువైన దానికంటే ఎక్కువ సమస్యగా మార్చగలవు.
ఆధునిక ప్రత్యామ్నాయాలు: `__slots__` ఇంకా ఉత్తమ ఎంపికేనా?
పైథాన్ ఎకోసిస్టమ్ అభివృద్ధి చెందింది మరియు తేలికపాటి వస్తువులను సృష్టించడానికి __slots__
మాత్రమే సాధనం కాదు. ఆధునిక పైథాన్ కోడ్ కోసం, మీరు ఈ అద్భుతమైన ప్రత్యామ్నాయాలను పరిగణించాలి.
`collections.namedtuple` మరియు `typing.NamedTuple`
నేమ్టూపుల్స్ అనేవి పేరున్న ఫీల్డ్లతో టూపుల్ సబ్క్లాస్లను సృష్టించడానికి ఒక ఫ్యాక్టరీ ఫంక్షన్. అవి అద్భుతంగా మెమరీ-సమర్థవంతమైనవి (స్లాటెడ్ వస్తువుల కంటే కూడా ఎక్కువ, ఎందుకంటే అవి లోపల టూపుల్స్) మరియు, ముఖ్యంగా, ఇమ్మ్యూటబుల్.
from typing import NamedTuple
# Creates an immutable class with type hints
class Point(NamedTuple):
x: int
y: int
p = Point(10, 20)
print(p.x) # 10
try:
p.x = 30 # Raises AttributeError: can't set attribute
except AttributeError as e:
print(e)
మీకు ఇమ్మ్యూటబుల్ డేటా కంటైనర్ అవసరమైతే, స్లాటెడ్ క్లాస్ కంటే NamedTuple
తరచుగా మెరుగైన మరియు సరళమైన ఎంపిక.
రెండు ప్రపంచాలలో ఉత్తమమైనది: `@dataclass(slots=True)`
పైథాన్ 3.7లో ప్రవేశపెట్టబడి మరియు పైథాన్ 3.10లో మెరుగుపరచబడిన డేటాక్లాస్లు ఒక గేమ్-ఛేంజర్. అవి __init__
, __repr__
మరియు __eq__
వంటి పద్ధతులను స్వయంచాలకంగా ఉత్పత్తి చేస్తాయి, బాయిలర్ప్లేట్ కోడ్ను గణనీయంగా తగ్గిస్తాయి.
ముఖ్యంగా, @dataclass
డెకరేటర్కు slots
ఆర్గ్యుమెంట్ ఉంది (పైథాన్ 3.10 నుండి అందుబాటులో ఉంది; పైథాన్ 3.8-3.9 కోసం అదే సౌలభ్యం కోసం థర్డ్-పార్టీ లైబ్రరీ అవసరం). మీరు slots=True
అని సెట్ చేసినప్పుడు, డేటాక్లాస్ నిర్వచించిన ఫీల్డ్ల ఆధారంగా స్వయంచాలకంగా __slots__
అట్రిబ్యూట్ను ఉత్పత్తి చేస్తుంది.
from dataclasses import dataclass
@dataclass(slots=True)
class DataPoint:
x: int
y: int
dp = DataPoint(10, 20)
print(dp) # Output: DataPoint(x=10, y=20) - nice repr for free!
print(hasattr(dp, '__dict__')) # Output: False - slots are enabled!
ఈ విధానం మీకు అన్ని ప్రపంచాలలో ఉత్తమమైన వాటిని అందిస్తుంది:
- చదవగల సామర్థ్యం మరియు సంక్షిప్తత: మాన్యువల్ క్లాస్ నిర్వచనం కంటే చాలా తక్కువ బాయిలర్ప్లేట్.
- సౌలభ్యం: స్వయంచాలకంగా రూపొందించబడిన ప్రత్యేక పద్ధతులు సాధారణ బాయిలర్ప్లేట్ను వ్రాయకుండా మిమ్మల్ని కాపాడతాయి.
- పనితీరు:
__slots__
యొక్క పూర్తి మెమరీ మరియు వేగ ప్రయోజనాలు. - టైప్ సేఫ్టీ: పైథాన్ యొక్క టైపింగ్ ఎకోసిస్టమ్తో సంపూర్ణంగా కలిసిపోతుంది.
పైథాన్ 3.10+లో వ్రాసిన కొత్త కోడ్ కోసం, సరళమైన, మ్యూటబుల్, మెమరీ-సమర్థవంతమైన డేటా-నిల్వ క్లాస్లను సృష్టించడానికి `@dataclass(slots=True)` మీ డిఫాల్ట్ ఎంపికగా ఉండాలి.
ముగింపు: ఒక నిర్దిష్ట పని కోసం శక్తివంతమైన సాధనం
__slots__
అనేది పనితీరు యొక్క సరిహద్దులను పెంచాల్సిన డెవలపర్ల కోసం శక్తివంతమైన సాధనాలను అందించాలనే పైథాన్ యొక్క డిజైన్ తత్వానికి నిదర్శనం. ఇది విచక్షణారహితంగా ఉపయోగించాల్సిన ఫీచర్ కాదు, బదులుగా ఒక నిర్దిష్ట మరియు సాధారణ సమస్యను పరిష్కరించడానికి ఒక పదునైన, ఖచ్చితమైన సాధనం: అనేక చిన్న వస్తువుల అధిక మెమరీ ఖర్చు.
__slots__
గురించి ముఖ్యమైన వాస్తవాలను తిరిగి పరిశీలిద్దాం:
- దీని ప్రాథమిక ప్రయోజనం మెమరీ వినియోగంలో గణనీయమైన తగ్గింపు, తరచుగా ఇన్స్టాన్స్ల పరిమాణాన్ని 40-50% తగ్గిస్తుంది. ఇది దీని యొక్క ముఖ్యమైన ఫీచర్.
- ఇది అట్రిబ్యూట్ యాక్సెస్ కోసం ద్వితీయ, మరింత నిరాడంబరమైన, వేగ పెరుగుదలను అందిస్తుంది, సాధారణంగా 10-20% ఉంటుంది.
- ప్రధాన రాజీ డైనమిక్ అట్రిబ్యూట్ కేటాయింపును కోల్పోవడం, ఇది కఠినమైన వస్తువు నిర్మాణాన్ని అమలు చేస్తుంది.
- ఇది ఇన్హెరిటెన్స్తో సంక్లిష్టతను పరిచయం చేస్తుంది, ప్రత్యేకించి బహుళ వారసత్వ సందర్భాలలో జాగ్రత్తగా డిజైన్ అవసరం.
-
ఆధునిక పైథాన్లో, `@dataclass(slots=True)` తరచుగా ఒక ఉన్నతమైన, మరింత సౌకర్యవంతమైన ప్రత్యామ్నాయం, ఇది
__slots__
ప్రయోజనాలను డేటాక్లాస్ల సౌందర్యంతో కలుపుతుంది.
ఆప్టిమైజేషన్ యొక్క గోల్డెన్ రూల్ ఇక్కడ వర్తిస్తుంది: ముందు ప్రొఫైల్ చేయండి. మ్యాజికల్ స్పీడ్అప్ ఆశించి మీ కోడ్బేస్ అంతటా __slots__
ని చల్లకండి. ఏ వస్తువులు ఎక్కువ మెమరీని వినియోగిస్తున్నాయో గుర్తించడానికి మెమరీ ప్రొఫైలింగ్ సాధనాలను ఉపయోగించండి. ఒక క్లాస్ మిలియన్ల సార్లు ఇన్స్టాన్షియేట్ చేయబడి, మెమరీని ఎక్కువగా వినియోగిస్తుందని మీరు కనుగొంటే, అప్పుడు—మరియు అప్పుడు మాత్రమే—__slots__
కోసం వెళ్ళాల్సిన సమయం. దాని శక్తిని మరియు దాని ప్రమాదాలను అర్థం చేసుకోవడం ద్వారా, మీరు ప్రపంచ ప్రేక్షకుల కోసం మరింత సమర్థవంతమైన మరియు స్కేలబుల్ పైథాన్ అప్లికేషన్లను నిర్మించడానికి దానిని సమర్థవంతంగా ఉపయోగించవచ్చు.