2013-07-05 4 views
11

कक्षा फू में एक बार है। बार तक लोड नहीं किया जाता है जब तक इसे एक्सेस नहीं किया जाता है। बार तक पहुंचने के लिए कोई ओवरहेड नहीं होना चाहिए।पायथन - वर्ग विशेषताओं की आलसी लोडिंग

class Foo(object): 

    def get_bar(self): 
     print "initializing" 
     self.bar = "12345" 
     self.get_bar = self._get_bar 
     return self.bar 

    def _get_bar(self): 
     print "accessing" 
     return self.bar 

क्या यह गेटटर विधि का उपयोग करने के बजाय गुणों या बेहतर, अभी तक विशेषताओं का उपयोग करके ऐसा कुछ करना संभव है?

लक्ष्य बाद के सभी पहुंच पर भूमि के ऊपर के बिना आलसी लोड करने के लिए है ...

+0

आप वर्णनकर्ता के साथ स्वचालित रूप से ऐसा कर सकते हैं: http://jeetworks.org/node/62 – schlamar

+1

WERKZEUG व्यापक साथ एक बेहतर कार्यान्वयन है टिप्पणियां: https://github.com/mitsuhiko/werkzeug/blob/10b4b8b6918a83712170fdaabd3ec61cf07f23ff/werkzeug/utils.py#L35 – schlamar

+0

यह भी देखें: [पायथन आलसी संपत्ति सजावट] (http://stackoverflow.com/questions/3012421/python- आलसी-संपत्ति-डेकोरेटर)। – detly

उत्तर

11

वर्तमान उत्तरों के साथ कुछ समस्याएं हैं। किसी संपत्ति के साथ समाधान के लिए आवश्यक है कि आप एक अतिरिक्त श्रेणी विशेषता निर्दिष्ट करें और प्रत्येक विशेषता पर इस विशेषता को जांचने का ओवरहेड है। __getattr__ के साथ समाधान में यह समस्या है कि यह इस विशेषता को पहली पहुंच तक छुपाता है। आत्मनिरीक्षण के लिए यह बुरा है और __dir__ के साथ कामकाज असुविधाजनक है।

दो प्रस्तावित लोगों की तुलना में बेहतर समाधान सीधे वर्णक का उपयोग कर रहा है। Werkzeug लाइब्रेरी पहले से ही werkzeug.utils.cached_property के रूप में एक समाधान है। यह एक सरल कार्यान्वयन है ताकि आप सीधे WERKZEUG निर्भरता के रूप में बिना इसका इस्तेमाल कर सकते हैं:

_missing = object() 

class cached_property(object): 
    """A decorator that converts a function into a lazy property. The 
    function wrapped is called the first time to retrieve the result 
    and then that calculated result is used the next time you access 
    the value:: 

     class Foo(object): 

      @cached_property 
      def foo(self): 
       # calculate something important here 
       return 42 

    The class has to have a `__dict__` in order for this property to 
    work. 
    """ 

    # implementation detail: this property is implemented as non-data 
    # descriptor. non-data descriptors are only invoked if there is 
    # no entry with the same name in the instance's __dict__. 
    # this allows us to completely get rid of the access function call 
    # overhead. If one choses to invoke __get__ by hand the property 
    # will still work as expected because the lookup logic is replicated 
    # in __get__ for manual invocation. 

    def __init__(self, func, name=None, doc=None): 
     self.__name__ = name or func.__name__ 
     self.__module__ = func.__module__ 
     self.__doc__ = doc or func.__doc__ 
     self.func = func 

    def __get__(self, obj, type=None): 
     if obj is None: 
      return self 
     value = obj.__dict__.get(self.__name__, _missing) 
     if value is _missing: 
      value = self.func(obj) 
      obj.__dict__[self.__name__] = value 
     return value 
+4

इसके साथ समस्या वेब ढांचे (वेर्कज़्यूग, डीजेगो, बोतल, पिरामिड, एट अल) के दायरे से बाहर है, यह धागे के साथ अच्छी तरह से काम नहीं करती है। Https://github.com/pydanny/cached-property/issues/6 देखें (जिसे हमने बंद किया) – pydanny

8

ज़रूर, सिर्फ अपनी संपत्ति की स्थापना की है एक उदाहरण विशेषता है कि बाद के उपयोग पर दिया जाता है:

class Foo(object): 
    _cached_bar = None 

    @property 
    def bar(self): 
     if not self._cached_bar: 
      self._cached_bar = self._get_expensive_bar_expression() 
     return self._cached_bar 

property वर्णनकर्ता एक डेटा डिस्क्रिप्टर है (यह __get__, __set__ और __delete__ डिस्क्रिप्टर हुक लागू करता है), इसलिए यह उदाहरण दिया जाएगा कि bar विशेषता उदाहरण पर मौजूद है, अंत परिणाम के साथ कि पाइथन उस विशेषता को अनदेखा करता है, इसलिए अलग से परीक्षण करने की आवश्यकता attrib प्रत्येक पहुंच पर ute। यदि आप एक __getattr__ दृष्टिकोण पसंद करते हैं

class CachedProperty(object): 
    def __init__(self, func, name=None): 
     self.func = func 
     self.name = name if name is not None else func.__name__ 
     self.__doc__ = func.__doc__ 

    def __get__(self, instance, class_): 
     if instance is None: 
      return self 
     res = self.func(instance) 
     setattr(instance, self.name, res) 
     return res 

class Foo(object): 
    @CachedProperty 
    def bar(self): 
     return self._get_expensive_bar_expression() 

(जो कुछ करना है:

आप यदि वह मौजूद है अपनी खुद की जानकारी, जिसका केवल लागू करता __get__ है, जो बिंदु पर पायथन वर्णनकर्ता से अधिक उदाहरण पर एक विशेषता का उपयोग करता है लिख सकते हैं इसके लिए कहते हैं), कि हो जाएगा:

class Foo(object): 
    def __getattr__(self, name): 
     if name == 'bar': 
      bar = self.bar = self._get_expensive_bar_expression() 
      return bar 
     return super(Foo, self).__getattr__(name) 

बाद पहुँच उदाहरण पर bar विशेषता मिलेगा और __getattr__ परामर्श नहीं किया जाएगा।

डेमो:

>>> class FooExpensive(object): 
...  def _get_expensive_bar_expression(self): 
...   print 'Doing something expensive' 
...   return 'Spam ham & eggs' 
... 
>>> class FooProperty(FooExpensive): 
...  _cached_bar = None 
...  @property 
...  def bar(self): 
...   if not self._cached_bar: 
...    self._cached_bar = self._get_expensive_bar_expression() 
...   return self._cached_bar 
... 
>>> f = FooProperty() 
>>> f.bar 
Doing something expensive 
'Spam ham & eggs' 
>>> f.bar 
'Spam ham & eggs' 
>>> vars(f) 
{'_cached_bar': 'Spam ham & eggs'} 
>>> class FooDescriptor(FooExpensive): 
...  bar = CachedProperty(FooExpensive._get_expensive_bar_expression, 'bar') 
... 
>>> f = FooDescriptor() 
>>> f.bar 
Doing something expensive 
'Spam ham & eggs' 
>>> f.bar 
'Spam ham & eggs' 
>>> vars(f) 
{'bar': 'Spam ham & eggs'} 

>>> class FooGetAttr(FooExpensive): 
...  def __getattr__(self, name): 
...   if name == 'bar': 
...    bar = self.bar = self._get_expensive_bar_expression() 
...    return bar 
...   return super(Foo, self).__getatt__(name) 
... 
>>> f = FooGetAttr() 
>>> f.bar 
Doing something expensive 
'Spam ham & eggs' 
>>> f.bar 
'Spam ham & eggs' 
>>> vars(f) 
{'bar': 'Spam ham & eggs'} 
+0

यह प्रत्येक पहुंच पर अतिरिक्त "अगर" के ओवरहेड जोड़ता है। क्या पहली बार संपत्ति को फिर से परिभाषित करना संभव है? –

+0

आपको वैसे भी किसी झंडे की आवश्यकता होगी, कुछ आपको बताएगा कि क्या आपने संपत्ति को तुरंत चालू कर दिया है या नहीं। –

+1

@whatscanasta: 'संपत्ति 'के साथ नहीं, क्योंकि पाइथन उदाहरण वर्णनों पर डेटा वर्णनकर्ता प्राथमिकता देता है। लेकिन '__getattr__' के साथ आप * कर सकते हैं (अपडेट देखें)। –

1

ज़रूर यह है, की कोशिश:

class Foo(object): 
    def __init__(self): 
     self._bar = None # Initial value 

    @property 
    def bar(self): 
     if self._bar is None: 
      self._bar = HeavyObject() 
     return self._bar 

ध्यान दें कि यह थ्रेड-सुरक्षित नहीं है। सीपीथन में जीआईएल है, इसलिए यह एक सापेक्ष मुद्दा है, लेकिन यदि आप इसे एक वास्तविक मल्टीथ्रेड पायथन स्टैक (कहें, ज्योथन) में उपयोग करने की योजना बना रहे हैं, तो आप लॉक सुरक्षा के कुछ रूपों को लागू करना चाहेंगे।

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