क्या मॉड्यूल में उसी तरह के गुण हो सकते हैं जैसे ऑब्जेक्ट कर सकते हैं?


98

अजगर गुणों के साथ, मैं इसे ऐसा बना सकता हूं

obj.y 

किसी मान को वापस करने के बजाय एक फ़ंक्शन को कॉल करता है।

क्या मॉड्यूल के साथ ऐसा करने का कोई तरीका है? मेरे पास एक मामला है जहां मैं चाहता हूं

module.y 

किसी फ़ंक्शन को कॉल करने के लिए, बल्कि वहां संग्रहीत मान को वापस करने के बजाय।


2
अधिक आधुनिक समाधान के लिए __getattr__एक मॉड्यूल देखें ।
विम

जवाबों:


56

नई शैली की कक्षाओं के केवल उदाहरणों में गुण हो सकते हैं। आप पायथन को यह विश्वास दिला सकते हैं कि इस तरह का उदाहरण एक मॉड्यूल है जिसमें इसे मारा गया है sys.modules[thename] = theinstance। इसलिए, उदाहरण के लिए, आपकी m.py मॉड्यूल फाइल हो सकती है:

import sys

class _M(object):
    def __init__(self):
        self.c = 0
    def afunction(self):
        self.c += 1
        return self.c
    y = property(afunction)

sys.modules[__name__] = _M()

2
क्या किसी और ने यह कोशिश की? जब मैंने इस कोड को एक फ़ाइल x.py में डाल दिया और इसे दूसरे से आयात किया, तब AttyError में xy परिणाम कहते हैं: 'noneType' ऑब्जेक्ट में कोई विशेषता 'c' नहीं है, क्योंकि _M का किसी भी तरह से कोई भी मूल्य नहीं है ...
Stephan202

3
दरअसल कोड दुभाषिया पर काम करता है। लेकिन जब मैं इसे एक फाइल में डालता हूं (कहते हैं, bowwow.py) और मैं इसे दूसरी फाइल (otherfile.py) से आयात करता हूं, तो यह अब काम नहीं करता ...
Stephan202

4
प्रश्न: क्या कोई विशेष लाभ (ओं) का उदाहरण के वर्ग को प्राप्त करने से होगा types.ModuleTypeजैसा कि @ अज्ञात अन्यथा बहुत समान उत्तर में दिखाया गया है?
मार्टिउ

11
नई शैली की कक्षाओं के केवल उदाहरणों में गुण हो सकते हैं। यही कारण है नहीं है: मॉड्यूल हैं में है कि वे के उदाहरण हैं, नई शैली वर्गों के उदाहरण builtins.moduleहै, जो खुद का एक उदाहरण है type(जो नई शैली वर्ग की परिभाषा है)। समस्या यह है कि गुण वर्ग पर होने चाहिए, उदाहरण नहीं: यदि आप करते हैं f = Foo(), f.some_property = property(...)तो यह उसी तरह से विफल हो जाएगा जैसे कि आप भोलेपन से इसे मॉड्यूल में रखते हैं। इसका समाधान यह है कि इसे कक्षा में रखा जाए, लेकिन जब से आप सभी मॉड्यूल संपत्ति नहीं चाहते हैं, तो आप उपवर्ग (अज्ञात का जवाब देखें)।
थानाटोस

3
@ जो, नाम के रिबॉन्डिंग पर globals()(कीज़ को बरकरार रखना, लेकिन मानों को बरकरार रखना None) का परिवर्तन sys.modulesएक पायथन 2 मुद्दा है - पायथन 3.4 इरादा के अनुसार काम करता है। यदि आपको Py2 में क्लास ऑब्जेक्ट तक पहुंचने की आवश्यकता है, _M._cls = _Mतो classस्टेटमेंट के ठीक बाद उदा जोड़ें (या इसे किसी अन्य नामस्थान में समान रूप से स्टैश करें) और इसे एक्सेस self._clsकरने की आवश्यकता वाले तरीकों के रूप में एक्सेस करें (यह type(self)ठीक हो सकता है, लेकिन यदि आप किसी भी उपवर्ग का नहीं करते हैं _M) ।
एलेक्स मार्टेली

54

मैं मॉड्यूल की सभी विशेषताओं को ठीक से इनहेरिट करने के लिए ऐसा करूंगा, और आइंस्टीन () द्वारा सही तरीके से पहचाना जा सकता है

import types

class MyModule(types.ModuleType):
    @property
    def y(self):
        return 5


>>> a=MyModule("test")
>>> a
<module 'test' (built-in)>
>>> a.y
5

और फिर आप इसे sys.modules में डाल सकते हैं:

sys.modules[__name__] = MyModule(__name__)  # remember to instantiate the class

यह केवल सबसे सरल मामलों के लिए काम करता है। संभावित मुद्दे हैं: (1) कुछ आयात सहायकों से अन्य विशेषताओं की भी अपेक्षा की जा सकती है, जैसे __file__कि मैन्युअल रूप से परिभाषित किया जाना है, (2) वर्ग वाले मॉड्यूल में किए गए आयात रन समय के दौरान "दृश्यमान" नहीं होंगे, आदि ...
tutuDajuju

1
यह एक उपवर्ग से प्राप्त करने के लिए आवश्यक नहीं है types.ModuleType, किसी भी (नई शैली) वर्ग करेंगे। वास्तव में क्या विशेष मॉड्यूल विशेषता है जहाँ आप विरासत की उम्मीद कर रहे हैं?
मार्टीन्यू

क्या होगा यदि मूल मॉड्यूल एक पैकेज है और मैं मूल मॉड्यूल के नीचे मॉड्यूल का उपयोग करना चाहता हूं?
kawing-chiu

2
@martineau आपके पास एक मॉड्यूल repr होगा, __init__उदाहरण के लिए, आप मॉड्यूल का नाम निर्दिष्ट कर सकते हैं और उपयोग करते समय आपको सही व्यवहार मिलेगा isinstance
विम

@wim: लिया गया अंक, हालांकि स्पष्ट रूप से कोई भी महत्वपूर्ण IMO नहीं लगता है।
मार्टीन्यू

34

चूंकि PEP 566 = = 3.7 में PEP लागू किया गया है, अब हम ऐसा कर सकते हैं

फ़ाइल: मॉड्यूल

def __getattr__(name):
    if name == 'y':
        return 3
    raise AttributeError(f"module '{__name__}' has no attribute '{name}'")

other = 4

उपयोग:

>>> import module
>>> module.y
3
>>> module.other
4
>>> module.nosuch
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "module.py", line 4, in __getattr__
    raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
AttributeError: module 'module' has no attribute 'nosuch'

ध्यान दें कि यदि आप को छोड़ देते हैं raise AttributeErrorमें __getattr__समारोह, इसके साथ समारोह समाप्त होता है इसका मतलब है return None, तो module.nosuchके एक मूल्य मिल जाएगा None


2
इसके आधार पर, मैंने एक और उत्तर जोड़ा: stackoverflow.com/a/58526852/2124834

3
यह एक संपत्ति का केवल आधा है। कोई सेटर नहीं।
विम

दुर्भाग्य से यह नहीं टूलींग इस तरह के गुण के बारे में पता करने के लिए कठिन लगता है ((?) Getattr अगर कोई नियमित सदस्य पाया जाता है केवल शुरू हो जाती है)
olejorgenb

9

जॉन लिन के जवाब पर आधारित :

def module_property(func):
    """Decorator to turn module functions into properties.
    Function names must be prefixed with an underscore."""
    module = sys.modules[func.__module__]

    def base_getattr(name):
        raise AttributeError(
            f"module '{module.__name__}' has no attribute '{name}'")

    old_getattr = getattr(module, '__getattr__', base_getattr)

    def new_getattr(name):
        if f'_{name}' == func.__name__:
            return func()
        else:
            return old_getattr(name)

    module.__getattr__ = new_getattr
    return func

उपयोग (प्रमुख अंडरस्कोर पर ध्यान दें) the_module.py:

@module_property
def _thing():
    return 'hello'

फिर:

import the_module

print(the_module.thing)  # prints 'hello'

मूल फ़ंक्शन से प्रॉपर्टी-आइस्ड फ़ंक्शन को अलग करने के लिए अग्रणी अंडरस्कोर आवश्यक है। मैं पहचानकर्ता को आश्वस्त करने का एक तरीका नहीं सोच सकता, क्योंकि सज्जाकार निष्पादन के समय के दौरान, इसे अभी तक नहीं सौंपा गया है।

ध्यान दें कि आईडीई को पता नहीं चलेगा कि संपत्ति मौजूद है और लाल लहराती दिखाई देगी।


महान! वर्ग संपत्ति के साथ तुलना में @property def x(self): return self._xमुझे लगता है कि def thing()अंडरस्कोर बिना अधिक पारंपरिक है। और क्या आप अपने जवाब में "मॉड्यूल प्रॉपर्टी सेटर" डेकोरेटर भी बना सकते हैं?
जॉन लिन

2
@ जॉनलीन, मैंने आपके def thing()सुझाव पर अमल करने का प्रयास किया । समस्या यह है कि __getattr__केवल लापता विशेषताओं के लिए बुलाया जाता है । लेकिन @module_property def thing(): …रन के बाद , the_module.thingपरिभाषित किया गया है, इसलिए गेटअटर को कभी नहीं बुलाया जाएगा। हमें किसी तरह thingडेकोरेटर में पंजीकरण करने की आवश्यकता है और फिर इसे मॉड्यूल के नाम स्थान से हटा दें। मैंने Noneडेकोरेटर से लौटने की कोशिश की , लेकिन फिर thingइसे परिभाषित किया गया None। एक कर सकता है, @module_property def thing(): … del thingलेकिन मुझे लगता है कि thing()एक समारोह के रूप में उपयोग करने से भी बदतर
बेन मार्स 17

ठीक है, मैं देख रहा हूं कि कोई "मॉड्यूल संपत्ति सेटर" नहीं है, न ही "मॉड्यूल __getattribute__"। धन्यवाद।
जॉन लिन

5

एक विशिष्ट उपयोग का मामला है: एक (विशाल) मौजूदा मॉड्यूल को कुछ (कुछ) गतिशील विशेषताओं के साथ समृद्ध करना - सभी मॉड्यूल सामान को क्लास लेआउट में बदले बिना। दुर्भाग्य से एक सबसे सरल मॉड्यूल वर्ग पैच की तरह sys.modules[__name__].__class__ = MyPropertyModuleविफल रहता है TypeError: __class__ assignment: only for heap types। इसलिए मॉड्यूल निर्माण को फिर से शुरू करने की आवश्यकता है।

यह दृष्टिकोण पायथन आयात हुक के बिना करता है, बस मॉड्यूल कोड के शीर्ष पर कुछ प्रोलॉग होने से:

# propertymodule.py
""" Module property example """

if '__orgmod__' not in globals():

    # constant prolog for having module properties / supports reload()

    print "PropertyModule stub execution", __name__
    import sys, types
    class PropertyModule(types.ModuleType):
        def __str__(self):
            return "<PropertyModule %r from %r>" % (self.__name__, self.__file__)
    modnew = PropertyModule(__name__, __doc__)
    modnew.__modclass__ = PropertyModule        
    modnew.__file__ = __file__
    modnew.__orgmod__ = sys.modules[__name__]
    sys.modules[__name__] = modnew
    exec sys._getframe().f_code in modnew.__dict__

else:

    # normal module code (usually vast) ..

    print "regular module execution"
    a = 7

    def get_dynval(module):
        return "property function returns %s in module %r" % (a * 4, module.__name__)    
    __modclass__.dynval = property(get_dynval)

उपयोग:

>>> import propertymodule
PropertyModule stub execution propertymodule
regular module execution
>>> propertymodule.dynval
"property function returns 28 in module 'propertymodule'"
>>> reload(propertymodule)   # AFTER EDITS
regular module execution
<module 'propertymodule' from 'propertymodule.pyc'>
>>> propertymodule.dynval
"property function returns 36 in module 'propertymodule'"

नोट: जैसे कुछ from propertymodule import dynvalपाठ्यक्रम की एक जमे हुए प्रतिलिपि का उत्पादन होगा - के अनुरूपdynval = someobject.dynval


1

एक छोटा जवाब: उपयोग proxy_tools

proxy_toolsपैकेज प्रदान करने के लिए प्रयास करता है @module_propertyकार्यक्षमता।

इसके साथ स्थापित होता है

pip install proxy_tools

@ मरीन के उदाहरण के एक मामूली संशोधन का उपयोग करते हुए, the_module.pyहम डालते हैं

from proxy_tools import module_property

@module_property
def thing():
    print(". ", end='')  # Prints ". " on each invocation
    return 'hello'

अब दूसरी स्क्रिप्ट से, मैं कर सकता हूं

import the_module

print(the_module.thing)
# . hello

अप्रत्याशित व्यवहार

यह समाधान बिना केवेट्स के नहीं है। अर्थात् the_module.thingहै एक स्ट्रिंग नहीं ! यह एक ऐसी proxy_tools.Proxyवस्तु है जिसके विशेष तरीकों को ओवरराइड किया गया है ताकि यह एक स्ट्रिंग की नकल करे। यहाँ कुछ बुनियादी परीक्षण दिए गए हैं जो इस बात की व्याख्या करते हैं:

res = the_module.thing
# [No output!!! Evaluation doesn't occur yet.]

print(type(res))
# <class 'proxy_tools.Proxy'>

print(isinstance(res, str))
# False

print(res)
# . hello

print(res + " there")
# . hello there

print(isinstance(res + "", str))
# . True

print(res.split('e'))
# . ['h', 'llo']

आंतरिक रूप से, मूल फ़ंक्शन को इसमें संग्रहीत किया जाता है the_module.thing._Proxy__local:

print(res._Proxy__local)
# <function thing at 0x7f729c3bf680>

आगे के विचार

ईमानदारी से, मैं इस बारे में चकित हूं कि मॉड्यूल में यह कार्यक्षमता क्यों नहीं है। मुझे लगता है कि इस मामले का क्रुक्स वर्ग the_moduleका एक उदाहरण है types.ModuleType। एक "मॉड्यूल संपत्ति" सेट करना इस वर्ग के उदाहरण पर एक संपत्ति स्थापित करने के लिए , बल्कि types.ModuleTypeवर्ग पर ही। अधिक जानकारी के लिए, यह उत्तर देखें ।

हम वास्तव types.ModuleTypeमें निम्नानुसार गुणों को लागू कर सकते हैं, हालांकि परिणाम महान नहीं हैं। हम सीधे निर्मित प्रकारों को संशोधित नहीं कर सकते, लेकिन हम उन्हें शाप दे सकते हैं:

# python -m pip install forbiddenfruit
from forbiddenfruit import curse
from types import ModuleType
# curse has the same signature as setattr.
curse(ModuleType, "thing2", property(lambda module: f'hi from {module.__name__}'))

यह हमें एक संपत्ति देता है जो सभी मॉड्यूल पर मौजूद है। हम सभी मॉड्यूलों में सेटिंग व्यवहार को तोड़ने के बाद से यह थोड़ा सा है:

import sys

print(sys.thing2)
# hi from sys

sys.thing2 = 5
# AttributeError: can't set attribute

1
यह कैसे बेहतर है सिर्फ मॉड्यूल को वास्तविक वर्ग का उदाहरण बनाने के लिए जैसा कि @ एलेक्स मार्टेली के उत्तर में दिखाया गया है?
21

1
आपने कुछ और कहा है जो मेरे लिए मायने नहीं रखता है। @module_propertyडेकोरेटर होने के बारे में इस व्यवसाय को लें । सामान्य तौर पर, निर्मित @propertyसज्जाकार का उपयोग तब किया जाता है जब किसी वर्ग को परिभाषित किया जाता है, न कि इसके उदाहरण के बाद इसे बनाया गया है, इसलिए मैं मानूंगा कि यह मॉड्यूल संपत्ति के लिए सही होगा और यह एलेक्स के उत्तर के साथ है - इस प्रश्न को याद करें "क्या मॉड्यूल में उसी तरह के गुण हो सकते हैं जैसे वस्तुएं कर सकती हैं?"। हालांकि यह है उन्हें बाद में जोड़ना संभव और मैं अपने पहले संशोधित किया टुकड़ा एक तरीका है कि किया जा सकता है वर्णन करने के लिए।
मार्टीन्यू

1
बेन: आपके ठोस उदाहरण में कोड को देखने के बाद, मुझे लगता है कि मुझे समझ में आ गया है कि आप अभी क्या प्राप्त कर रहे हैं। मुझे यह भी लगता है कि मैं हाल ही में मॉड्यूल गुणों के लिए कुछ को लागू करने के लिए एक तकनीक पर ठोकर खाई है कि एलेक्स के जवाब में जैसे एक वर्ग उदाहरण के साथ मॉड्यूल को प्रतिस्थापित करने की आवश्यकता नहीं है, हालांकि इस बिंदु पर मुझे यकीन नहीं है कि अगर ऐसा करने का एक तरीका है यह एक डेकोरेटर के माध्यम से - अगर मैं कोई प्रगति करता हूं तो आपको वापस मिल जाएगा।
मार्टीन्यू

1
ठीक है, यहाँ एक अन्य प्रश्न के उत्तर का लिंक दिया गया है जिसमें मुख्य विचार है।
मार्टीन्यू

1
ठीक है, कम से कम एक के मामले में cached_module_property, तथ्य यह है कि __getattr__()अब नहीं बुलाया जाएगा अगर विशेषता परिभाषित हो जाता है सहायक है। (जैसा functools.cached_propertyपूरा होता है)।
मार्टिन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.