মডিউলগুলির যেমন বৈশিষ্ট্যগুলি পারে তেমন বৈশিষ্ট্য থাকতে পারে?


102

পাইথন বৈশিষ্ট্য সহ, আমি এটি এমন করতে পারি

obj.y 

একটি মান ফিরিয়ে দেওয়ার চেয়ে একটি ফাংশন কল করে।

মডিউলগুলির সাথে এটি করার কোনও উপায় আছে? আমার একটা মামলা আছে যেখানে আমি চাই

module.y 

একটি ফাংশন কল করার জন্য, কেবল সেখানে সঞ্চিত মান ফিরিয়ে দেওয়া।


4
আরও আধুনিক সমাধানের জন্য __getattr__একটি মডিউল দেখুন ।
wim

উত্তর:


55

কেবলমাত্র নতুন স্টাইলের ক্লাসগুলির উদাহরণগুলির বৈশিষ্ট্য থাকতে পারে। পাইথনকে বিশ্বাস করে তুলতে পারেন যে এ জাতীয় উদাহরণটি স্ট্যাশ করে এটি একটি মডিউল 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()

4
অন্য কেউ এই চেষ্টা করে? আমি এই কোডটি যখন একটি ফাইল x.py এ রেখেছি এবং এটি অন্য ফাইল থেকে আমদানি করি, তখন অ্যাট্রিবিউটআরারে xy ফলাফল কল করে: 'ননটাইপ' অবজেক্টটির কোনও বৈশিষ্ট্য নেই 'সি', যেহেতু
_ এম এর

4
প্রকৃতপক্ষে কোড দোভাষীর উপর কাজ করে। তবে আমি যখন এটি কোনও ফাইলে রাখি (বলুন, বোনাউপি.পি) এবং আমি এটি অন্য একটি ফাইল (otherfile.py) থেকে আমদানি করি, তখন এটি আর কাজ করে না ...
স্টেফান 202

4
প্রশ্ন: types.ModuleType@ অজানা এর অনুরূপ অনুরূপ উত্তরে যেমন দেখানো হয়েছে সে থেকে উদাহরণের শ্রেণি প্রাপ্ত করার কোনও বিশেষ সুবিধা (গুলি) থাকবে ?
মার্টিনিউ

12
কেবলমাত্র নতুন স্টাইলের ক্লাসগুলির উদাহরণগুলির বৈশিষ্ট্য থাকতে পারে। এটি কারণ নয়: মডিউলগুলি নতুন স্টাইলের ক্লাসগুলির উদাহরণ, সেগুলিতে এটি উদাহরণস্বরূপ builtins.module, যা নিজেই এর একটি উদাহরণ type(যা নতুন শৈলিক শ্রেণীর সংজ্ঞা)। সমস্যা হল বৈশিষ্ট্য বর্গ, না উদাহরণস্বরূপ হওয়া আবশ্যক হল: যদি আপনি না f = Foo(), f.some_property = property(...)এটা একই ভাবে ব্যর্থ করব যেন আপনি naively একটি মডিউল মধ্যে লাগাতে হবে। সমাধানটি এটি ক্লাসে রাখা, তবে যেহেতু আপনি সম্পত্তি যুক্ত সমস্ত মডিউল চান না তাই আপনি সাবক্লাস (অজানাটির উত্তর দেখুন)।
থানাটোস

4
@ জো, নামের পুনর্নির্মাণের ক্ষেত্রে globals()(কীগুলি অক্ষত রাখার সাথে সাথে মানগুলি পুনরায় সেট করা None) এর পরিবর্তনটি sys.modulesপাইথন 2 ইস্যু - পাইথন ৩.৪ উদ্দেশ্য হিসাবে কাজ করে। আপনার যদি পাই 2 তে ক্লাস অবজেক্টে অ্যাক্সেসের প্রয়োজন হয়, যেমন _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

এটি কেবলমাত্র সহজতম ক্ষেত্রে কাজ করে বলে মনে হচ্ছে। সম্ভাব্য সমস্যাগুলি হ'ল: (১) কিছু আমদানি সহায়তাও অন্যান্য বৈশিষ্ট্যগুলি যেমন __file__ম্যানুয়ালি সংজ্ঞায়িত করতে হবে এমন আশা করতে পারে, (২) শ্রেণি সমন্বিত মডিউলটিতে তৈরি আমদানি রান চলাকালীন সময়ে "দৃশ্যমান" হবে না ইত্যাদি ...
টুটুডাজুজু

4
এটা একটা উপশ্রেণী থেকে আহরণ করা জরুরি নয় types.ModuleType, কোনো (নতুন স্টাইলের) বর্গ করতে হবে। আপনি উত্তরাধিকার সূত্রে আশা করছেন এমন কোনও বিশেষ মডিউল বৈশিষ্ট্যগুলি কী?
মার্টিনিউ

মূল মডিউলটি যদি একটি প্যাকেজ হয় এবং আমি মূল মডিউলটির নীচে মডিউলগুলি অ্যাক্সেস করতে চাই তবে কী হবে?
কাওয়িং-চিউ

4
@ মার্টিনো আপনার কাছে একটি মডিউল পুনরায় পোস্টার থাকবে, আপনি যখন __init__উদাহরণস্বরূপ মডিউলটির নামটি নির্দিষ্ট করতে পারবেন এবং ব্যবহার করার সময় আপনি সঠিক আচরণ পাবেন isinstance
wim

@ উইম: পয়েন্টগুলি নেওয়া হয়েছে, যদিও সত্যই কোনওটিই এই সমস্ত গুরুত্বপূর্ণ আইএমও বলে মনে হয় না।
মার্টিনো

35

যেহেতু পিইপি 562 পাইথন> = 3.7 এ প্রয়োগ করা হয়েছে, এখন আমরা এটি করতে পারি

ফাইল: মডিউল.পি

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


4
: এই উপর ভিত্তি করে, আমি অন্য উত্তর যোগ stackoverflow.com/a/58526852/2124834

4
এটি একটি সম্পত্তির অর্ধেক মাত্র। সেটার নেই।
উইম

দুর্ভাগ্যক্রমে এই জাতীয় বৈশিষ্ট্য (?) সম্পর্কে টুলিং সচেতন করা কঠিন বলে মনে হয় না ( নিয়মিত সদস্য না পাওয়া গেলেই গ্যাটট্রার কেবল অনুরোধ করা হয়)
ওলেজোরেনব

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'

মূল ফাংশন থেকে সম্পত্তি-ized ফাংশনকে পৃথক করতে নেতৃস্থানীয় আন্ডারস্কোর প্রয়োজন। আমি শনাক্তকারীকে পুনরায় নিয়োগের কোনও উপায়ের কথা ভাবতে পারি না, যেহেতু সাজসজ্জার কার্যকর করার সময়, এটি এখনও নির্ধারিত হয়নি।

নোট করুন যে আইডিইগুলি জানবে না যে সম্পত্তিটি বিদ্যমান এবং এটি লাল তাকাবে।


দুর্দান্ত! শ্রেণীর সম্পত্তির সাথে তুলনা করা @property def x(self): return self._xআমি মনে করি def thing()আন্ডারস্কোর ছাড়াই বেশি প্রচলিত। এবং আপনি কি আপনার উত্তরে একটি "মডিউল সম্পত্তি সেটার" সজ্জা তৈরি করতে পারেন?
জন লিন

4
@ জনলিন, আমি আপনার def thing()পরামর্শটি বাস্তবায়নের চেষ্টা করেছি । সমস্যাটি হ'ল __getattr__কেবল অনুপস্থিত বৈশিষ্ট্যের জন্য ডাকা হয় । তবে @module_property def thing(): …রান করার পরে the_module.thingসংজ্ঞায়িত করা হয়, সুতরাং গেটআটার কখনই ডাকা হবে না। আমাদের কোনওভাবে ডেকরেটারে নিবন্ধন করতে হবে thingএবং তারপরে এটি মডিউলটির নামস্থান থেকে মুছতে হবে। আমি Noneসাজসজ্জার কাছ থেকে ফিরে চেষ্টা , কিন্তু তারপর thingহিসাবে সংজ্ঞায়িত করা হয় None। একটি করতে পারে @module_property def thing(): … del thingতবে আমি thing()একটি ফাংশন হিসাবে ব্যবহারের চেয়ে খারাপ দেখতে পেয়েছি
বেন ম্যারেস

ঠিক আছে আমি দেখতে পাচ্ছি কোন "মডিউল সম্পত্তি সেটটার", না "মডিউল __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

4
@ অ্যালেক্স মার্তেলির উত্তরে দেখানো মডিউলটিকে বাস্তব শ্রেণীর উদাহরণ হিসাবে তৈরি করার চেয়ে কীভাবে এটি আরও ভাল?
মার্টিনিউ

4
আপনি অন্য কিছু বলেছেন যা আমার কাছে বোধগম্য নয়। @module_propertyসাজসজ্জা রাখার বিষয়ে এই ব্যবসায়টি নিন । সাধারণভাবে বলতে গেলে, বিল্ট-ইন @propertyডেকরেটর ব্যবহার করা হয় যখন কোনও শ্রেণি সংজ্ঞায়িত করা হয়, এর কোনও উদাহরণ তৈরি হওয়ার পরে নয়, তাই আমি ধরে নিই যে এটি একটি মডিউল সম্পত্তি হিসাবে সত্য হবে এবং এটি অ্যালেক্সের উত্তর সহ - এই প্রশ্নটি জিজ্ঞাসা করে জিজ্ঞাসা করুন "মডিউলগুলির যেমন বৈশিষ্ট্য একইভাবে থাকতে পারে?" তবে এগুলি পরে যুক্ত করা সম্ভব এবং আমি কী করতে পারি তার উপায় চিত্রিত করার জন্য আমার আগের স্নিপেটটি সংশোধন করেছি।
মার্টিনিউ

4
বেন: আপনার কংক্রিটের উদাহরণে কোডটি দেখার পরে, আমি মনে করি আপনি এখন কী পাচ্ছেন তা আমি বুঝতে পেরেছি। আমি আরও মনে করি যে আমি সম্প্রতি মডিউলটির বৈশিষ্ট্যগুলির অনুরূপ কিছু বাস্তবায়নের জন্য একটি কৌশলতে হোঁচট খেয়েছি যা অ্যালেক্সের উত্তরের মতো একটি শ্রেণীর উদাহরণ দিয়ে মডিউলটি প্রতিস্থাপনের প্রয়োজন হবে না, যদিও আমি এই মুহুর্তে নিশ্চিত না যে যদি কিছু করার উপায় থাকে তবে এটি একটি সাজসজ্জার মাধ্যমে - আমি কোন অগ্রগতি করলে আপনার কাছে ফিরে আসব।
মার্টিনিউ

4
ঠিক আছে, এখানে অন্য একটি প্রশ্নের উত্তরের লিঙ্ক যা মূল ধারণাটি রয়েছে।
মার্টিনিউ

4
ভাল, কমপক্ষে একটি ক্ষেত্রে, গুণটি নির্ধারিত হয়ে গেলে আর cached_module_propertyসেই সত্যটি __getattr__()আর বলা হবে না। (যা functools.cached_propertyঅর্জন করে তার সমান )।
মার্টিনো
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.