একটি মডিউলে __getattr__


127

__getattr__একটি শ্রেণিতে একটি মডিউলে কী সমমানের প্রয়োগ করতে পারে ?

উদাহরণ

মডিউলটির স্থিতিযুক্ত সংজ্ঞায়িত বৈশিষ্ট্যগুলিতে অস্তিত্ব নেই এমন কোনও ফাংশন কল করার সময়, আমি সেই মডিউলটিতে একটি শ্রেণির একটি উদাহরণ তৈরি করতে চাই এবং মডিউলটিতে অ্যাট্রিবিউট অনুসন্ধানে ব্যর্থ হিসাবে একই নামটির সাথে পদ্ধতিটি অনুরোধ করব।

class A(object):
    def salutation(self, accusative):
        print "hello", accusative

# note this function is intentionally on the module, and not the class above
def __getattr__(mod, name):
    return getattr(A(), name)

if __name__ == "__main__":
    # i hope here to have my __getattr__ function above invoked, since
    # salutation does not exist in the current namespace
    salutation("world")

যা দেয়:

matt@stanley:~/Desktop$ python getattrmod.py 
Traceback (most recent call last):
  File "getattrmod.py", line 9, in <module>
    salutation("world")
NameError: name 'salutation' is not defined

2
আমি সম্ভবত শোকের উত্তর নিয়ে যাব, কারণ এটি সমস্ত পরিস্থিতিতে কাজ করে (যদিও এটি কিছুটা অগোছালো এবং আরও ভাল কাজ করা যেতে পারে)। হার্ভার্ড এস এবং এস লটের সুন্দর পরিষ্কার উত্তর রয়েছে তবে সেগুলি ব্যবহারিক সমাধান নয়।
ম্যাট জয়েনার

1
আপনি এমনকি কোনও বৈশিষ্ট্য অ্যাক্সেস করা আপনার ক্ষেত্রে নেই, তাই আপনি একবারে দুটি আলাদা জিনিস জিজ্ঞাসা করছেন। সুতরাং প্রধান প্রশ্নটি কোনটি আপনি চান। আপনি কি salutationবিশ্বব্যাপী বা স্থানীয় নেমস্পেসে উপস্থিত থাকতে চান (উপরের কোডটি যা করার চেষ্টা করছে) বা আপনি যখন মডিউলটিতে ডট অ্যাক্সেস করেন তখন নামগুলির গতিশীল অনুসন্ধান চান? এটি দুটি ভিন্ন জিনিস।
লেনার্ট রেজেব্রো

মজাদার প্রশ্ন, আপনি এটিকে কীভাবে সামনে এলেন?
ক্রিস ওয়েসলিং


1
__getattr__মডিউলগুলি পাইথন
৩. 3.

উত্তর:


41

কিছুক্ষণ আগে, গাইডো ঘোষণা করেছিল যে নতুন-স্টাইলের ক্লাসগুলি বাইপাসে __getattr__এবং বিশেষ পদ্ধতিতে সমস্ত বিশেষ পদ্ধতি অনুসন্ধান রয়েছে__getattribute__ । ডিন্ডার পদ্ধতিগুলি পূর্বে মডিউলগুলিতে কাজ করেছিল - উদাহরণস্বরূপ, আপনি কেবল প্রাসঙ্গিক পরিচালক হিসাবে মডিউলটি কেবল সংজ্ঞা দিয়ে __enter__এবং __exit__সেই কৌশলগুলি ভাঙ্গার আগে ব্যবহার করতে পারেন ।

সম্প্রতি কিছু historicalতিহাসিক বৈশিষ্ট্য আবার ফিরে এসেছে, __getattr__তাদের মধ্যে মডিউল রয়েছে এবং তাই বিদ্যমান হ্যাক ( sys.modulesআমদানির সময় একটি শ্রেণীর সাথে নিজেকে প্রতিস্থাপনকারী একটি মডিউল ) আর প্রয়োজন হবে না no

পাইথন ৩.7++ তে আপনি কেবল একটি সুস্পষ্ট উপায় ব্যবহার করেন। মডিউলটিতে অ্যাট্রিবিউট অ্যাক্সেস কাস্টমাইজ করতে __getattr__মডিউল স্তরে এমন একটি ফাংশন সংজ্ঞায়িত করুন যা একটি যুক্তি (গুণকের নাম) গ্রহণ করবে এবং গুণিত মানটি ফিরিয়ে দেবে বা একটি বাড়িয়ে তুলবে AttributeError:

# my_module.py

def __getattr__(name: str) -> Any:
    ...

এটি "থেকে" আমদানিতে হুককেও অনুমতি দেবে, অর্থাত্ আপনি যেমন বিবৃতি দেওয়ার জন্য গতিশীলভাবে উত্পন্ন জিনিসগুলি ফিরে আসতে পারেন from my_module import whatever

সম্পর্কিত নোটে, মডিউল getattr সহ __dir__আপনি প্রতিক্রিয়া জানাতে মডিউল স্তরে কোনও ক্রিয়াও সংজ্ঞায়িত করতে পারেন dir(my_module)। বিস্তারিত জানার জন্য পিইপি 562 দেখুন ।


1
যদি আমি গতিশীলভাবে মডিউল তৈরি করে m = ModuleType("mod")সেট করি m.__getattr__ = lambda attr: return "foo"; যাইহোক, আমি যখন চালানো from mod import foo, আমি পেতে TypeError: 'module' object is not iterable
weberc2

@ ওয়েবারসি 2: এটি তৈরি করুন m.__getattr__ = lambda attr: "foo", এর সাথে আপনাকে মডিউলটির জন্য একটি এন্ট্রি সংজ্ঞায়িত করতে হবে sys.modules['mod'] = m। পরে, এর সাথে কোনও ত্রুটি নেই from mod import foo
মার্টিনো

উইম: আপনি গতিশীলভাবে গণনা করা মানগুলিও পেতে পারেন - যেমন মডিউল-স্তরের সম্পত্তি থাকা one যাকে my_module.whateverএটি লেখার অনুমতি দেয় (এর পরে import my_module)।
মার্টিনো

115

এখানে দুটি মূল সমস্যা রয়েছে যা:

  1. __xxx__ পদ্ধতিগুলি কেবল ক্লাসে দেখানো হয়
  2. TypeError: can't set attributes of built-in/extension type 'module'

(1) মানে যে কোনও সমাধানের ক্ষেত্রে কোন মডিউলটি পরীক্ষা করা হচ্ছে তাও ট্র্যাক করে রাখতে হবে, অন্যথায় প্রতিটি মডিউলের ক্ষেত্রে উদাহরণ-প্রতিস্থাপনের আচরণ থাকবে; এবং (2) এর অর্থ হ'ল (1) এমনকি সম্ভব নয় ... কমপক্ষে সরাসরি নয়।

ভাগ্যক্রমে, sys.modules সেখানে কীভাবে যায় তার সম্পর্কে পিক নয়, তবে একটি মোড়ক কাজ করবে তবে কেবল মডিউল অ্যাক্সেসের জন্য (যেমন import somemodule; somemodule.salutation('world'); একই-মডিউল অ্যাক্সেসের জন্য আপনাকে বিকল্পের শ্রেণি থেকে পদ্ধতিগুলি globals()ঝাঁকিয়ে পড়তে হবে এবং তাদের সাথে আইয়ারে যুক্ত করতে হবে ) ক্লাসে কাস্টম পদ্ধতি (আমি ব্যবহার করতে পছন্দ করি .export()) বা জেনেরিক ফাংশন (যেমন ইতিমধ্যে উত্তর হিসাবে তালিকাভুক্ত রয়েছে) দিয়ে একটি জিনিস মনে রাখা উচিত: যদি মোড়ক প্রতিবারই একটি নতুন উদাহরণ তৈরি করে, এবং গ্লোবালগুলি সমাধান না হয়, আপনি সম্পূর্ণ ভিন্ন আচরণের সাথে সমাপ্ত হন Oh ওহ, এবং আপনি একইসাথে উভয়ই ব্যবহার করতে পাবেন না - এটি এক বা অন্যটি।


হালনাগাদ

থেকে গাইডো van Rossum :

আসলে একটি হ্যাক যা মাঝেমধ্যে ব্যবহৃত হয় এবং প্রস্তাবিত হয়: একটি মডিউল পছন্দসই কার্যকারিতা সহ একটি শ্রেণীর সংজ্ঞা দিতে পারে এবং তারপরে শেষে, সেটিকে ক্লাসের উদাহরণ দিয়ে (অথবা ক্লাসের সাথে, যদি আপনি জোর করেন তবে) sys.modules এ নিজেকে প্রতিস্থাপন করতে পারে if , তবে এটি সাধারণত কম দরকারী)। উদাহরণ:

# module foo.py

import sys

class Foo:
    def funct1(self, <args>): <code>
    def funct2(self, <args>): <code>

sys.modules[__name__] = Foo()

এটি কাজ করে কারণ আমদানি যন্ত্রপাতি সক্রিয়ভাবে এই হ্যাকটিকে সক্ষম করে এবং এর চূড়ান্ত পদক্ষেপটি লোড করার পরে, সিএস.মডিউলগুলি থেকে প্রকৃত মডিউলটিকে টেনে আনে। (এটি কোনও দুর্ঘটনা নয় The হ্যাকটি অনেক আগেই প্রস্তাব করা হয়েছিল এবং আমরা সিদ্ধান্ত নিয়েছি যে আমদানি যন্ত্রপাতিগুলিতে এটি সমর্থন করার জন্য আমাদের যথেষ্ট পছন্দ হয়েছিল))

সুতরাং আপনি যা চান তা অর্জনের প্রতিষ্ঠিত উপায় হ'ল আপনার মডিউলে একটি ক্লাস তৈরি করা এবং মডিউলের শেষ কাজ হিসাবে sys.modules[__name__]আপনার শ্রেণীর উদাহরণটি প্রতিস্থাপন করুন - এবং এখন আপনি প্রয়োজন হিসাবে __getattr__/ __setattr__/ দিয়ে খেলতে পারেন __getattribute__


দ্রষ্টব্য 1 : আপনি যদি এই কার্যকারিতাটি ব্যবহার করেন তবে sys.modulesঅ্যাসাইনমেন্টটি তৈরি করার পরে মডিউলটিতে থাকা অন্য কিছু যেমন গ্লোবাল, অন্যান্য ফাংশন ইত্যাদি নষ্ট হবে - সুতরাং নিশ্চিত হয়ে নিন যে প্রয়োজনীয় সবকিছু প্রতিস্থাপন শ্রেণীর ভিতরে রয়েছে।

নোট 2 : সমর্থন করার জন্য from module import *আপনাকে অবশ্যই __all__শ্রেণিতে সংজ্ঞায়িত করতে হবে ; উদাহরণ স্বরূপ:

class Foo:
    def funct1(self, <args>): <code>
    def funct2(self, <args>): <code>
    __all__ = list(set(vars().keys()) - {'__module__', '__qualname__'})

আপনার পাইথন সংস্করণের উপর নির্ভর করে বাদ পড়ার জন্য অন্যান্য নাম থাকতে পারে __all__set()যদি পাইথন 2 সামঞ্জস্য প্রয়োজন নেই বাদ দেওয়া যেতে পারে।


3
এটি কাজ করে কারণ আমদানি যন্ত্রপাতি সক্রিয়ভাবে এই হ্যাকটিকে সক্রিয় করছে, এবং এটির চূড়ান্ত পদক্ষেপটি লোড করার পরে, সিএস.মডিউলগুলি থেকে প্রকৃত মডিউলটিকে টেনে আনে, ডক্সে কোথাও উল্লেখ করা হয়েছে?
পাইওটর ডব্রোগস্ট

4
এখন এই হ্যাকটি ব্যবহার করে আমি আরও স্বাচ্ছন্দ্য বোধ করছি, এটি "অর্ধ-অনুমোদিত" হিসাবে বিবেচনা করছি :)
মার্ক নুনবার্গ

3
এটি চটজলদি জিনিসগুলি করছে, যেমন import sysদেওয়ার Noneজন্য sys। আমি অনুমান করছি যে এই হ্যাকটি পাইথন 2 তে অনুমোদিত নয়
asmeurer

3
@ এসম্যুরার: এর কারণটি বোঝার জন্য (এবং একটি সমাধান) প্রশ্নটি দেখুন সিস.মোডিয়ুলস [__ নাম__] এ নিয়োগের পরে __ নাম__ এর মান কেন পরিবর্তন হচ্ছে?
মার্টিনো

1
@ কারমা: আমি অজগর মডিউলগুলি আরও ক্লাসের উত্তরাধিকারের মডেলটিতে আরও সরাসরি অংশ নিতে পারার কথা বলে যাচ্ছিল এমন কিছু বর্ধনের কথা মনে করতে চাইছি, তবে এখনও এই পদ্ধতিটি কাজ করে এবং সমর্থিত।
ইথান ফুরম্যান

48

এটি একটি হ্যাক, তবে আপনি একটি ক্লাসের সাথে মডিউলটি মোড়ানো করতে পারেন:

class Wrapper(object):
  def __init__(self, wrapped):
    self.wrapped = wrapped
  def __getattr__(self, name):
    # Perform custom logic here
    try:
      return getattr(self.wrapped, name)
    except AttributeError:
      return 'default' # Some sensible default

sys.modules[__name__] = Wrapper(sys.modules[__name__])

10
সুন্দর এবং নোংরা: ডি
ম্যাট জেন্ডার

এটি কার্যকর হতে পারে তবে এটি সম্ভবত লেখকের আসল সমস্যার সমাধান নয়।
দাসিচ

12
"মে কাজ" এবং "সম্ভবত না" খুব সহায়ক নয়। এটি একটি হ্যাক / ট্রিক, তবে এটি কাজ করে এবং প্রশ্নের দ্বারা উত্পন্ন সমস্যাটি সমাধান করে।
হাভার্ড এস

6
এটি অন্যান্য মডিউলগুলিতে কাজ করবে যা আপনার মডিউল আমদানি করে এবং এতে অস্তিত্বের অ্যাক্সেসগুলি অ্যাক্সেস করে, এটি এখানে প্রকৃত কোড উদাহরণের জন্য কাজ করবে না। গ্লোবাল () অ্যাক্সেস sys.modules মাধ্যমে যায় না।
মারিয়াস গেডমিনাস

5
দুর্ভাগ্যক্রমে এটি বর্তমান মডিউলটির জন্য কাজ করে না বা সম্ভবত কোনও পরে স্ট্যাক অ্যাক্সেস করা স্টাফের জন্য import *
ম্যাট জয়েনার 6:25

19

আমরা সাধারণত এটি সেভাবে করি না।

আমরা যা করি তা হ'ল এটি।

class A(object):
....

# The implicit global instance
a= A()

def salutation( *arg, **kw ):
    a.salutation( *arg, **kw )

কেন? যাতে অন্তর্নিহিত গ্লোবাল দৃষ্টান্তটি দৃশ্যমান।

উদাহরণস্বরূপ, randomমডিউলটি দেখুন, যেখানে আপনি "সাধারণ" র্যান্ডম সংখ্যার জেনারেটর চান সেখানে ব্যবহারের ক্ষেত্রে কিছুটা সহজ করার জন্য একটি অন্তর্নিহিত বিশ্বব্যাপী দৃষ্টান্ত তৈরি করে।


আপনি যদি সত্যিই উচ্চাভিলাষী হন তবে আপনি ক্লাস তৈরি করতে এবং তার সমস্ত পদ্ধতির মাধ্যমে পুনরাবৃত্তি করতে এবং প্রতিটি পদ্ধতির জন্য একটি মডিউল স্তরের ফাংশন তৈরি করতে পারেন।
পল ফিশার

@ পল ফিশার: সমস্যা অনুযায়ী ক্লাসটি ইতিমধ্যে বিদ্যমান। ক্লাসের সমস্ত পদ্ধতি এক্সপোজ করা ভাল ধারণা নাও হতে পারে। সাধারণত এই উন্মুক্ত পদ্ধতিগুলি হ'ল "সুবিধা" পদ্ধতি। অন্তর্নিহিত গ্লোবাল দৃষ্টান্তের জন্য সমস্ত উপযুক্ত নয়।
এসলট

13

@ হ্যাভার্ড এস প্রস্তাবিত কিসের অনুরূপ, একটি ক্ষেত্রে যেখানে আমার কোনও মডিউলে কিছু জাদু বাস্তবায়ন করা প্রয়োজন (যেমন __getattr__), আমি একটি নতুন শ্রেণি সংজ্ঞায়িত করব যা উত্তরাধিকার সূত্রে প্রাপ্ত হয়েছে types.ModuleTypeএবং এতে sys.modulesরেখেছি (সম্ভবত আমার কাস্টম যেখানে মডিউলটি প্রতিস্থাপন করবে)ModuleType সংজ্ঞায়িত )।

এটির মোটামুটি শক্তিশালী প্রয়োগের জন্য ওয়ার্কজেগের মূল __init__.pyফাইলটি দেখুন ।


8

এটি হ্যাকিশ, কিন্তু ...

import types

class A(object):
    def salutation(self, accusative):
        print "hello", accusative

    def farewell(self, greeting, accusative):
         print greeting, accusative

def AddGlobalAttribute(classname, methodname):
    print "Adding " + classname + "." + methodname + "()"
    def genericFunction(*args):
        return globals()[classname]().__getattribute__(methodname)(*args)
    globals()[methodname] = genericFunction

# set up the global namespace

x = 0   # X and Y are here to add them implicitly to globals, so
y = 0   # globals does not change as we iterate over it.

toAdd = []

def isCallableMethod(classname, methodname):
    someclass = globals()[classname]()
    something = someclass.__getattribute__(methodname)
    return callable(something)


for x in globals():
    print "Looking at", x
    if isinstance(globals()[x], (types.ClassType, type)):
        print "Found Class:", x
        for y in dir(globals()[x]):
            if y.find("__") == -1: # hack to ignore default methods
                if isCallableMethod(x,y):
                    if y not in globals(): # don't override existing global names
                        toAdd.append((x,y))


for x in toAdd:
    AddGlobalAttribute(*x)


if __name__ == "__main__":
    salutation("world")
    farewell("goodbye", "world")

এটি বিশ্বব্যাপী নেমস্পেসের সমস্ত বস্তুর উপরে পুনরাবৃত্তি করে কাজ করে। আইটেমটি একটি শ্রেণি হয়, এটি শ্রেণীর বৈশিষ্ট্যগুলি পুনরাবৃত্তি করে। যদি বৈশিষ্ট্যটি কলযোগ্য হয় তবে এটি এটিকে একটি কার্য হিসাবে বিশ্বব্যাপী নেমস্পেসে যুক্ত করে।

এটি "__" সমন্বিত সমস্ত বৈশিষ্ট্যকে উপেক্ষা করে।

আমি এটি প্রোডাকশন কোডে ব্যবহার করব না তবে এটি আপনাকে শুরু করা উচিত।


2
আমি আমার প্রতি হাভার্ড এস এর উত্তর পছন্দ করি, কারণ এটি অনেকটা পরিষ্কার পরিচ্ছন্ন বলে মনে হয়, তবে এটি সরাসরি জিজ্ঞাসিত প্রশ্নের উত্তর দেয়।
শোক করুন

আমি শেষ পর্যন্ত যা দিয়েছি এটি তার অনেক কাছাকাছি। এটি সামান্য অগোছালো তবে একই মডিউলের মধ্যে গ্লোবালগুলি () দিয়ে সঠিকভাবে কাজ করে।
ম্যাট জয়েনার

1
আমার কাছে মনে হচ্ছে এই উত্তরটি ঠিক তেমনটি নয় যা জিজ্ঞাসা করা হয়েছিল যা "মডিউলের স্থিতিযুক্ত সংজ্ঞায়িত বৈশিষ্ট্যগুলির মধ্যে উপস্থিত না এমন কোনও ফাংশনটি কল করার সময়" কারণ এটি নিঃশর্তভাবে কাজ করছে এবং প্রতিটি সম্ভাব্য শ্রেণীর পদ্ধতি যুক্ত করছে। এটি এমন কোনও মডিউল র‌্যাপার ব্যবহার করে স্থির করা যেতে পারে যা AddGlobalAttribute()মডিউল স্তরের কেবল তখনই হয় AttributeError- @ হ্যাভার্ড এস এর যুক্তির বিপরীতে। যদি আমার কোন সুযোগ থাকে তবে আমি এটি পরীক্ষা করে নেব এবং নিজের হাইব্রিড উত্তর যুক্ত করব যদিও ওপি ইতিমধ্যে এই উত্তরটি গ্রহণ করেছে।
মার্টিনো

আমার আগের মন্তব্যে আপডেট করুন। আমি এখন বুঝতে পেরেছি যে NameErrorগ্লোবাল (মডিউল) নেমস্পেসের ব্যতিক্রমগুলি বাদ দেওয়া খুব শক্ত (অনুমিত?) - যা ব্যাখ্যা করে যে এই উত্তর কেন প্রতিটি সম্ভাব্য কেসকে সামনে রেখে আবশ্যক বর্তমান বৈশ্বিক নেমস্পেসে খুঁজে পাওয়া প্রতিটি সম্ভাবনার জন্য কল করতে যোগ করে।
মার্টিনিউ

5

এখানে আমার নিজের নম্র অবদান - @ হ্যাভার্ড এস এর উচ্চ রেট দেওয়া উত্তরের সামান্য শোভন, তবে কিছুটা আরও স্পষ্ট করা (যাতে এটি @ এস.লোটের কাছে গ্রহণযোগ্য হতে পারে, যদিও এটি অপটির পক্ষে যথেষ্ট ভাল নয়):

import sys

class A(object):
    def salutation(self, accusative):
        print "hello", accusative

class Wrapper(object):
    def __init__(self, wrapped):
        self.wrapped = wrapped

    def __getattr__(self, name):
        try:
            return getattr(self.wrapped, name)
        except AttributeError:
            return getattr(A(), name)

_globals = sys.modules[__name__] = Wrapper(sys.modules[__name__])

if __name__ == "__main__":
    _globals.salutation("world")

-3

আপনার ক্লাস রয়েছে এমন আপনার মডিউল ফাইল তৈরি করুন। মডিউলটি আমদানি করুন। getattrআপনি সবেমাত্র আমদানি করা মডিউলটিতে চালান । আপনি ব্যবহার করে একটি গতিশীল আমদানি করতে পারেন__import__ sys.modules থেকে মডিউলটি ।

আপনার মডিউলটি এখানে some_module.py:

class Foo(object):
    pass

class Bar(object):
    pass

এবং অন্য মডিউলে:

import some_module

Foo = getattr(some_module, 'Foo')

গতিশীলভাবে এটি করা:

import sys

__import__('some_module')
mod = sys.modules['some_module']
Foo = getattr(mod, 'Foo')

1
আপনি এখানে একটি পৃথক প্রশ্নের উত্তর দিচ্ছেন।
মারিয়াস গেডমিনাস
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.