2012-06-13 11 views
45

क्या कोई जादू विधि है जो असाइनमेंट ऑपरेटर को अधिभारित कर सकती है, जैसे __assign__(self, new_value)?क्या पाइथन असाइनमेंट को अधिभारित करना संभव है?

मैं एक उदाहरण के लिए एक फिर से बाँध न करे करना चाहते हैं:

class Protect(): 
    def __assign__(self, value): 
    raise Exception("This is an ex-parrot") 

var = Protect() # once assigned... 
var = 1   # this should raise Exception() 

क्या यह संभव है? क्या यह पागल है? क्या मुझे दवा पर होना चाहिए?

+1

उपयोग के मामले में क्या है? – msw

+0

केस का उपयोग करें: लोग मेरी सेवा API का उपयोग करके छोटी स्क्रिप्ट लिखने जा रहे हैं, और मैं उन्हें आंतरिक डेटा बदलने से रोकना चाहता हूं और इस परिवर्तन को अगली लिपि में प्रचारित करना चाहता हूं। – Caruccio

+3

पायथन स्पष्ट रूप से वादा करता है कि एक दुर्भावनापूर्ण या अज्ञानी कोडर को पहुंच से रोका जाएगा। अन्य भाषाएं आपको अज्ञानता के कारण कुछ प्रोग्रामर त्रुटि से बचने की अनुमति देती हैं, लेकिन लोगों के पास उनके चारों ओर कोड करने की अनोखी क्षमता है। – msw

उत्तर

7

मुझे नहीं लगता कि यह संभव है। जिस तरह से मैं इसे देखता हूं, एक चर के लिए असाइनमेंट उस ऑब्जेक्ट को कुछ भी नहीं करता है जिसे पहले संदर्भित किया गया था: यह केवल वैरिएबल "पॉइंट" को एक अलग ऑब्जेक्ट पर है।

In [3]: class My(): 
    ...:  def __init__(self, id): 
    ...:   self.id=id 
    ...: 

In [4]: a = My(1) 

In [5]: b = a 

In [6]: a = 1 

In [7]: b 
Out[7]: <__main__.My instance at 0xb689d14c> 

In [8]: b.id 
Out[8]: 1 # the object is unchanged! 

हालांकि, अगर आप __setitem__() या __setattr__() तरीकों कि एक अपवाद जुटाने के साथ एक आवरण वस्तु बनाने के द्वारा वांछित व्यवहार की नकल, और "अपरिवर्तनीय" सामान अंदर रख सकते हैं।

25

नहीं, क्योंकि असाइनमेंट language intrinsic है जिसमें एक संशोधन हुक नहीं है।

+2

के लिए एक आम दृष्टिकोण है आश्वस्त रहें, यह पायथन 4.x में नहीं होगा। –

+4

अब मैं मौजूदा क्षेत्र के उप-वर्गीकरण और प्रतिस्थापन के लिए एक पीईपी लिखने के लिए प्रेरित हूं। – zigg

2

कोई वहाँ नहीं

सोचो इसके बारे में, अपने उदाहरण में आप एक नया मान नाम वर rebinding कर रहे हैं। आप वास्तव में सुरक्षा के उदाहरण को छू नहीं रहे हैं।

यदि आप जिस नाम को पुनर्वित्त करना चाहते हैं वह वास्तव में किसी अन्य इकाई की संपत्ति है iee myobj.var तो आप इकाई की संपत्ति/विशेषता को मान निर्दिष्ट करने से रोक सकते हैं। लेकिन मुझे लगता है कि आप अपने उदाहरण से क्या नहीं चाहते हैं।

+0

लगभग वहाँ! मैंने मॉड्यूल के '__dict __.__ setattr__' को अधिभारित करने का प्रयास किया लेकिन' मॉड्यूल .__ dict__' स्वयं केवल पढ़ने के लिए है। साथ ही, टाइप करें (mymodule) == <टाइप 'मॉड्यूल'>, और यह उदाहरण योग्य नहीं है। – Caruccio

39

जिस तरीके से आप इसका वर्णन करते हैं वह बिल्कुल संभव नहीं है। किसी नाम पर असाइनमेंट पायथन की मूलभूत विशेषता है और इसके व्यवहार को बदलने के लिए कोई हुक प्रदान नहीं किया गया है।

हालांकि, कक्षा उदाहरण में किसी सदस्य को असाइनमेंट.__setattr__() ओवरराइड करके नियंत्रित किया जा सकता है।

class MyClass(object): 
    def __init__(self, x): 
     self.x = x 
     self._locked = True 
    def __setattr__(self, name, value): 
     if self.__dict__.get("_locked") and name == "x": 
      raise AttributeError("MyClass does not allow assignment to .x member") 
     self.__dict__[name] = value 

>>> m = MyClass(3) 
>>> m.x 
3 
>>> m.x = 4 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 7, in __setattr__ 
AttributeError: MyClass does not allow assignment to .x member 

नोट एक सदस्य चर, _locked, को नियंत्रित करता है कि काम की अनुमति दी है कि क्या होता है। आप मूल्य को अपडेट करने के लिए इसे अनलॉक कर सकते हैं।

+1

गेटटर के साथ '@ प्रॉपर्टी 'का उपयोग करना लेकिन कोई सेटटर छद्म-ओवरलोड असाइनमेंट का एक समान तरीका नहीं है। – jtpereyda

2

वैश्विक नामस्थान में यह संभव नहीं है, लेकिन Protect ऑब्जेक्ट को बनाए जाने से ऑब्जेक्ट के कई उदाहरणों को रोकने के लिए आप अधिक उन्नत पायथन मेटाप्रोग्रामिंग का लाभ उठा सकते हैं। Singleton pattern इसका अच्छा उदाहरण है।

सिंगलटन के मामले में आप यह सुनिश्चित करेंगे कि एक बार तत्काल हो, भले ही मूल चर संदर्भ को संदर्भित किया जाए, फिर भी ऑब्जेक्ट जारी रहेगा। किसी भी बाद के उदाहरण सिर्फ एक ही वस्तु का संदर्भ वापस कर देंगे।

इस पैटर्न के बावजूद, आप कभी भी वैश्विक चर नाम को फिर से सौंपने से रोकने में सक्षम नहीं होंगे।

+0

एक सिंगलटन पर्याप्त नहीं है, क्योंकि 'var = 1' सिंगलटन तंत्र को कॉल नहीं करता है। – Caruccio

+0

समझा। अगर मैं स्पष्ट नहीं था तो मैं क्षमा चाहता हूं। एक सिंगलटन किसी ऑब्जेक्ट के आगे उदाहरणों को रोक देगा (उदा। 'संरक्षित() ') बनने से। मूल रूप से असाइन किए गए नाम की रक्षा करने का कोई तरीका नहीं है (उदा। 'Var')। – jathanism

1

एक बदसूरत समाधान विनाशक पर फिर से सौंपना है। लेकिन यह कोई असली अधिभार असाइनमेंट नहीं है।

import copy 
global a 

class MyClass(): 
    def __init__(self): 
      a = 1000 
      # ... 

    def __del__(self): 
      a = copy.copy(self) 


a = MyClass() 
a = 1 
2

शीर्ष-स्तरीय नामस्थान का उपयोग करना, यह असंभव है।जब आप

`var = 1` 

चलाने यह कुंजी var और वैश्विक शब्दकोश में मूल्य 1 संग्रहीत करता है। यह globals().__setitem__('var', 1) पर कॉल करने के बराबर है। समस्या यह है कि आप वैश्विक शब्दकोश को एक चल रही स्क्रिप्ट में प्रतिस्थापित नहीं कर सकते हैं (संभवतः आप ढेर के साथ गड़बड़ कर सकते हैं, लेकिन यह एक अच्छा विचार नहीं है)। हालांकि आप द्वितीयक नेमस्पेस में कोड निष्पादित कर सकते हैं, और इसके ग्लोबल्स के लिए एक कस्टम शब्दकोश प्रदान कर सकते हैं।

class myglobals(dict): 
    def __setitem__(self, key, value): 
     if key=='val': 
      raise TypeError() 
     dict.__setitem__(self, key, value) 

myg = myglobals() 
dict.__setitem__(myg, 'val', 'protected') 

import code 
code.InteractiveConsole(locals=myg).interact() 

यह एक आरईपीएल जो लगभग सामान्य रूप से चल रही है, लेकिन चर val स्थापित करने के लिए किसी भी प्रयास से इनकार कर ऊपर सक्रिय हो जाएगा। आप execfile(filename, myg) का भी उपयोग कर सकते हैं। ध्यान दें कि यह दुर्भावनापूर्ण कोड से सुरक्षा नहीं करता है।

+0

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

1

हां, यह संभव है, आप ast संशोधित करके __assign__ को संभाल सकते हैं।

pip install assign

टेस्ट के साथ:

class T(): 
    def __assign__(self, v): 
     print('called with %s' % v) 
b = T() 
c = b 

आप

>>> import magic 
>>> import test 
called with c 

परियोजना मिल जाएगा https://github.com/RyanKung/assign पर है और सरल सार: https://gist.github.com/RyanKung/4830d6c8474e6bcefa4edd13f122b4df

+0

ऐसा कुछ है जो मुझे नहीं मिलता है ... क्या यह 'प्रिंट' नहीं होना चाहिए (जिसे% s '% self' कहा जाता है)? – zezollo

संबंधित मुद्दे