উদাহরণস্বরূপ পদ্ধতির সাজসজ্জাকারী কি ক্লাসে অ্যাক্সেস করতে পারবেন?


109

আমার নীচের মত মোটামুটি কিছু আছে। মূলত এর সংজ্ঞা অনুসারে উদাহরণ পদ্ধতিতে ব্যবহৃত একটি ডিকোরিটার থেকে আমার উদাহরণ পদ্ধতির ক্লাসটি অ্যাক্সেস করা দরকার।

def decorator(view):
    # do something that requires view's class
    print view.im_class
    return view

class ModelA(object):
    @decorator
    def a_method(self):
        # do some stuff
        pass

কোডটি যেমনটি দেয়:

অ্যাট্রিবিউটআরার: 'ফাংশন' অবজেক্টের কোনও 'im_class' নেই

আমি অনুরূপ প্রশ্ন / উত্তরগুলি পেয়েছি - পাইথন ডেকোরেটর ফাংশনটি ভুলে যায় যে এটি একটি শ্রেণীর অন্তর্ভুক্ত এবং পাইথন ডিকোরিটারে ক্লাস পান - তবে এগুলি নির্ভর করে প্রথম প্যারামিটার ছিনিয়ে নিয়ে রান-টাইমে উদাহরণটি ধরবে। আমার ক্ষেত্রে, আমি পদ্ধতিটি তার শ্রেণি থেকে সংগৃহীত তথ্যের ভিত্তিতে কল করব, সুতরাং আমি কোনও কল আসার অপেক্ষা করতে পারব না।

উত্তর:


68

আপনি যদি পাইথন ২.6 ব্যবহার করেন বা তার পরে আপনি কোনও শ্রেণি ডেকরেটার ব্যবহার করতে পারেন, সম্ভবত এর মতো কিছু (সতর্কতা: অরীক্ষিত কোড)।

def class_decorator(cls):
   for name, method in cls.__dict__.iteritems():
        if hasattr(method, "use_class"):
            # do something with the method and class
            print name, cls
   return cls

def method_decorator(view):
    # mark the method as something that requires view's class
    view.use_class = True
    return view

@class_decorator
class ModelA(object):
    @method_decorator
    def a_method(self):
        # do some stuff
        pass

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

ক্লাসটি তৈরি হওয়ার পরে ক্লাস ডেকোরেটর তারপরে সমস্ত পদ্ধতির মধ্য দিয়ে যায় এবং চিহ্নিত পদ্ধতিগুলিতে যা কিছু প্রয়োজন হয় তা করে।

আপনি যদি সমস্ত পদ্ধতিগুলিকে প্রভাবিত করতে চান তবে আপনি মেথড ডেকরেটরটি ফেলে রেখে কেবল ক্লাস ডেকোরেটর ব্যবহার করতে পারেন।


2
ধন্যবাদ আমি মনে করি এটিই সেই পথে যাবেন। যে কোনও শ্রেণীর জন্য কোডের একটি মাত্র অতিরিক্ত লাইন আমি এই ডেকরেটরটি ব্যবহার করতে চাই। আমি কাস্টম মেটাক্লাস ব্যবহার করতে পারি এবং নতুন সময়ে এই একই চেকটি সম্পাদন করতে পারি ...?
কার্ল জি

3
স্ট্যাটিকমেডোথ বা ক্লাসমেডোথের সাহায্যে যে কেউ এই ব্যবহার করতে চাইছেন তারা এই পিইপিটি পড়তে চাইবেন: পাইথন.আর / দেবদেব / পেপস / পেপ ০২২২ এটি নিশ্চিত নয় যে আপনি কোনও শ্রেণি / স্থির পদ্ধতিতে কোনও বৈশিষ্ট্য নির্ধারণ করতে পারবেন না এবং আমি মনে করি তারা গলগল করে যখন কোনও ফাংশনে প্রয়োগ করা হয় তখন কোনও কাস্টম ফাংশন বৈশিষ্ট্য আপ করুন।
কার্ল জি

আমার ডিবিএম ভিত্তিক ওআরএম-র জন্য আমি যা খুঁজছিলাম, ধন্যবাদ ... ডুড
কোयोট 21

inspect.getmro(cls)উত্তরাধিকার সমর্থন করার জন্য আপনার ক্লাস ডেকরেটারের সমস্ত বেস ক্লাসগুলি প্রক্রিয়া করার জন্য ব্যবহার করা উচিত ।
schlamar

1
উহু, আসলে এটা দেখে মনে হচ্ছে inspectউদ্ধার করতে stackoverflow.com/a/1911287/202168
Anentropic

16

পাইথন ৩.6 থেকে object.__set_name__আপনি এটি খুব সাধারণ উপায়ে সম্পন্ন করতে ব্যবহার করতে পারেন । দস্তাবেজটিতে বলা হয়েছে যে __set_name__"নিজস্ব শ্রেণীর মালিক তৈরি হওয়ার সময় বলা হয়েছিল "। এখানে একটি উদাহরণ:

class class_decorator:
    def __init__(self, fn):
        self.fn = fn

    def __set_name__(self, owner, name):
        # do something with owner, i.e.
        print(f"decorating {self.fn} and using {owner}")
        self.fn.class_name = owner.__name__

        # then replace ourself with the original method
        setattr(owner, name, self.fn)

লক্ষ্য করুন যে এটি শ্রেণি তৈরির সময় বলা হয়েছিল:

>>> class A:
...     @class_decorator
...     def hello(self, x=42):
...         return x
...
decorating <function A.hello at 0x7f9bedf66bf8> and using <class '__main__.A'>
>>> A.hello
<function __main__.A.hello(self, x=42)>
>>> A.hello.class_name
'A'
>>> a = A()
>>> a.hello()
42

ক্লাসগুলি কীভাবে তৈরি করা হয় এবং বিশেষত ঠিক কখন __set_name__ডাকা হয় সে সম্পর্কে আপনি যদি আরও জানতে চান তবে আপনি "ক্লাস অবজেক্ট তৈরি করা" সংক্রান্ত ডকুমেন্টেশনটি উল্লেখ করতে পারেন ।


1
এটি পরামিতিগুলির সাথে সজ্জা ব্যবহারকারীর মতো দেখতে কেমন হবে? যেমন@class_decorator('test', foo='bar')
ভাগ্যডোনাল্ড

2
@luckydonald আপনি এটা স্বাভাবিক অনুরূপ যোগাযোগ করতে পারেন আলোকচিত্রী যে আর্গুমেন্ট গ্রহণ করা । কেবল আছেdef decorator(*args, **kwds): class Descriptor: ...; return Descriptor
ম্যাট এডিং

বাহ, আপনাকে অনেক ধন্যবাদ. __set_name__যদিও আমি দীর্ঘদিন ধরে পাইথন ৩. using++ ব্যবহার করছি সে সম্পর্কে জানতাম না ।
কাওয়িং-চিউ

এই পদ্ধতির একটি অপূর্ণতা রয়েছে: স্থির চেকার এটিকে মোটেই বোঝে না। মাইপি ভাববে যে helloএটি কোনও পদ্ধতি নয়, পরিবর্তে এটি কোনও ধরণের অবজেক্ট class_decorator
কাভিং-চিউ

@ কাওয়িং-চিউ যদি অন্য কোনও কাজ না করে তবে আপনি সঠিক সাজসজ্জা করে সাধারণ সাজসজ্জার হিসাবে if TYPE_CHECKINGসংজ্ঞা class_decoratorহিসাবে ব্যবহার করতে পারেন ।
tyrion

15

অন্যরা যেমন উল্লেখ করেছে যে, সাজসজ্জার ডাক দেওয়ার সময় ক্লাস তৈরি হয়নি। যাইহোক , সাজসজ্জার প্যারামিটারগুলির সাথে ফাংশন অবজেক্টটি বয়ান করা সম্ভব, তারপরে মেটাক্লাসের __new__পদ্ধতিতে ফাংশনটি পুনরায় সাজাইয়া রাখা । __dict__কমপক্ষে আমার জন্য আপনাকে ফাংশনের বৈশিষ্ট্যটি সরাসরি অ্যাক্সেস করতে হবে, এর func.foo = 1ফলে একটি অ্যাট্রিবিউটআরারের ফলস্বরূপ।


6
setattrঅ্যাক্সেসের পরিবর্তে ব্যবহার করা উচিত__dict__
স্ক্ল্যামার

7

মার্ক যেমন পরামর্শ দেয়:

  1. যে কোনও সাজসজ্জারকে বলা হয় পূর্বে ক্লাসটি নির্মিত, তাই এটি সাজসজ্জার অজানা unknown
  2. আমরা ট্যাগ করতে পারি এই পদ্ধতিগুলিতে এবং পরে কোনও প্রয়োজনীয় পোস্ট-প্রক্রিয়া তৈরি করতে পারি।
  3. পোস্ট-প্রসেসিংয়ের জন্য আমাদের কাছে দুটি বিকল্প রয়েছে: ক্লাস সংজ্ঞা শেষে স্বয়ংক্রিয়ভাবে বা অ্যাপ্লিকেশনটি চালানোর আগে কোথাও। আমি বেস ক্লাস ব্যবহার করে 1 ম বিকল্পটি পছন্দ করি তবে আপনি ২ য় পন্থাটিও অনুসরণ করতে পারেন।

এই কোডটি দেখায় যে এটি কীভাবে স্বয়ংক্রিয় পোস্ট-প্রসেসিং ব্যবহার করে কাজ করতে পারে:

def expose(**kw):
    "Note that using **kw you can tag the function with any parameters"
    def wrap(func):
        name = func.func_name
        assert not name.startswith('_'), "Only public methods can be exposed"

        meta = func.__meta__ = kw
        meta['exposed'] = True
        return func

    return wrap

class Exposable(object):
    "Base class to expose instance methods"
    _exposable_ = None  # Not necessary, just for pylint

    class __metaclass__(type):
        def __new__(cls, name, bases, state):
            methods = state['_exposed_'] = dict()

            # inherit bases exposed methods
            for base in bases:
                methods.update(getattr(base, '_exposed_', {}))

            for name, member in state.items():
                meta = getattr(member, '__meta__', None)
                if meta is not None:
                    print "Found", name, meta
                    methods[name] = member
            return type.__new__(cls, name, bases, state)

class Foo(Exposable):
    @expose(any='parameter will go', inside='__meta__ func attribute')
    def foo(self):
        pass

class Bar(Exposable):
    @expose(hide=True, help='the great bar function')
    def bar(self):
        pass

class Buzz(Bar):
    @expose(hello=False, msg='overriding bar function')
    def bar(self):
        pass

class Fizz(Foo):
    @expose(msg='adding a bar function')
    def bar(self):
        pass

print('-' * 20)
print("showing exposed methods")
print("Foo: %s" % Foo._exposed_)
print("Bar: %s" % Bar._exposed_)
print("Buzz: %s" % Buzz._exposed_)
print("Fizz: %s" % Fizz._exposed_)

print('-' * 20)
print('examine bar functions')
print("Bar.bar: %s" % Bar.bar.__meta__)
print("Buzz.bar: %s" % Buzz.bar.__meta__)
print("Fizz.bar: %s" % Fizz.bar.__meta__)

আউটপুট ফলন:

Found foo {'inside': '__meta__ func attribute', 'any': 'parameter will go', 'exposed': True}
Found bar {'hide': True, 'help': 'the great bar function', 'exposed': True}
Found bar {'msg': 'overriding bar function', 'hello': False, 'exposed': True}
Found bar {'msg': 'adding a bar function', 'exposed': True}
--------------------
showing exposed methods
Foo: {'foo': <function foo at 0x7f7da3abb398>}
Bar: {'bar': <function bar at 0x7f7da3abb140>}
Buzz: {'bar': <function bar at 0x7f7da3abb0c8>}
Fizz: {'foo': <function foo at 0x7f7da3abb398>, 'bar': <function bar at 0x7f7da3abb488>}
--------------------
examine bar functions
Bar.bar: {'hide': True, 'help': 'the great bar function', 'exposed': True}
Buzz.bar: {'msg': 'overriding bar function', 'hello': False, 'exposed': True}
Fizz.bar: {'msg': 'adding a bar function', 'exposed': True}

এই উদাহরণে নোট করুন:

  1. আমরা যেকোন স্বেচ্ছাসেবী পরামিতি সহ যে কোনও ক্রিয়াকলাপকে টীকা দিতে পারি।
  2. প্রতিটি শ্রেণীর নিজস্ব উন্মুক্ত পদ্ধতি রয়েছে।
  3. আমরা উন্মুক্ত পদ্ধতিও উত্তরাধিকারী হতে পারি।
  4. এক্সপোজিং বৈশিষ্ট্য আপডেট হওয়ার সাথে সাথে পদ্ধতিগুলি ওভাররাইড হতে পারে।

আশাকরি এটা সাহায্য করবে


4

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

class Decorator(object):
    def __init__(self,decoratee_enclosing_class):
        self.decoratee_enclosing_class = decoratee_enclosing_class
    def __call__(self,original_func):
        def new_function(*args,**kwargs):
            print 'decorating function in ',self.decoratee_enclosing_class
            original_func(*args,**kwargs)
        return new_function


class Bar(object):
    @Decorator('Bar')
    def foo(self):
        print 'in foo'

class Baz(object):
    @Decorator('Baz')
    def foo(self):
        print 'in foo'

print 'before instantiating Bar()'
b = Bar()
print 'calling b.foo()'
b.foo()

ছাপে:

before instantiating Bar()
calling b.foo()
decorating function in  Bar
in foo

এছাড়াও, সজ্জকারগুলিতে ব্রুস একেলের পৃষ্ঠা দেখুন।


আমার হতাশাজনক উপসংহারটি নিশ্চিত করার জন্য ধন্যবাদ যে এটি সম্ভব নয়। আমি এমন একটি স্ট্রিংও ব্যবহার করতে পারি যা মডিউল / শ্রেণি ('মডিউল.ক্লাস') পুরোপুরি যোগ্য করে তুলেছিল, ক্লাসগুলি সমস্ত সম্পূর্ণ লোড না হওয়া পর্যন্ত স্ট্রিংগুলি গুলি সঞ্চয় করে, তারপরে আমদানি করে আমার নিজের ক্লাসগুলি পুনরুদ্ধার করে। এটি আমার কাজটি সম্পাদন করার জন্য একটি দুর্ভাগ্যজনকভাবে অ-শুকনা উপায় বলে মনে হচ্ছে।
কার্ল জি

এই ধরণের সাজসজ্জার জন্য আপনার কোনও ক্লাস ব্যবহার করার দরকার নেই: অলঙ্কারীয় কার্যকারিতাটি হ'ল ডেকরেটার ফাংশনের অভ্যন্তরে নেস্টেড ফাংশনগুলির একটি অতিরিক্ত স্তর ব্যবহার করা। তবে, আপনি যদি ক্লাসগুলি নিয়ে যান, সজ্জাটিকে নিজেরাই "স্ট্যান্ডার্ড" @decorator('Bar')হিসাবে দেখানোর জন্য , যেমন এর বিপরীতে , শ্রেণীর নামে মূলধনটি ব্যবহার না করা ভাল হতে পারে @Decorator('Bar')
এরিক কাপলুন

4

ফ্লাস্ক-ক্লাসিটি কী করে তা অস্থায়ী ক্যাশে তৈরি করে যা এটি পদ্ধতিতে সঞ্চয় করে, তারপরে এটি অন্য কিছু ব্যবহার করে (সত্য যে ফ্লাস্ক একটি ক্লাস ব্যবহার করে ক্লাসগুলি নিবন্ধিত করবে)register বর্গ পদ্ধতি) আসলে পদ্ধতি গোপন করতে।

আপনি এই প্যাটার্নটি পুনরায় ব্যবহার করতে পারেন, এবার একটি মেটাক্লাস ব্যবহার করে যাতে আপনি আমদানির সময় পদ্ধতিটি মোড়ানো করতে পারেন।

def route(rule, **options):
    """A decorator that is used to define custom routes for methods in
    FlaskView subclasses. The format is exactly the same as Flask's
    `@app.route` decorator.
    """

    def decorator(f):
        # Put the rule cache on the method itself instead of globally
        if not hasattr(f, '_rule_cache') or f._rule_cache is None:
            f._rule_cache = {f.__name__: [(rule, options)]}
        elif not f.__name__ in f._rule_cache:
            f._rule_cache[f.__name__] = [(rule, options)]
        else:
            f._rule_cache[f.__name__].append((rule, options))

        return f

    return decorator

প্রকৃত শ্রেণিতে (আপনি একটি মেটাক্লাস ব্যবহার করে এটি করতে পারেন):

@classmethod
def register(cls, app, route_base=None, subdomain=None, route_prefix=None,
             trailing_slash=None):

    for name, value in members:
        proxy = cls.make_proxy_method(name)
        route_name = cls.build_route_name(name)
        try:
            if hasattr(value, "_rule_cache") and name in value._rule_cache:
                for idx, cached_rule in enumerate(value._rule_cache[name]):
                    # wrap the method here

সূত্র: https://github.com/apiguy/flask-classy/blob/master/flask_classy.py


এটি একটি কার্যকর প্যাটার্ন, তবে এটি কোনও পদ্ধতির সজ্জাকারকে প্রয়োগ করা পদ্ধতির
পিতামাত্ত

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

3

সমস্যাটি হ'ল যখন সাজসজ্জারকে শ্রেণি বলা হয় তখনও অস্তিত্ব নেই। এটা চেষ্টা কর:

def loud_decorator(func):
    print("Now decorating %s" % func)
    def decorated(*args, **kwargs):
        print("Now calling %s with %s,%s" % (func, args, kwargs))
        return func(*args, **kwargs)
    return decorated

class Foo(object):
    class __metaclass__(type):
        def __new__(cls, name, bases, dict_):
            print("Creating class %s%s with attributes %s" % (name, bases, dict_))
            return type.__new__(cls, name, bases, dict_)

    @loud_decorator
    def hello(self, msg):
        print("Hello %s" % msg)

Foo().hello()

এই প্রোগ্রাম আউটপুট হবে:

Now decorating <function hello at 0xb74d35dc>
Creating class Foo(<type 'object'>,) with attributes {'__module__': '__main__', '__metaclass__': <class '__main__.__metaclass__'>, 'hello': <function decorated at 0xb74d356c>}
Now calling <function hello at 0xb74d35dc> with (<__main__.Foo object at 0xb74ea1ac>, 'World'),{}
Hello World

আপনি দেখতে পাচ্ছেন, আপনি যা চান তা করতে আপনাকে আলাদা উপায় বের করতে হবে।


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

ডিজি জেনুইন: ফাংশনটি কেবলমাত্র বলা হয় এবং এটি সম্পূর্ণরূপে তৈরি হওয়ার পরে কেবল ফাংশনটি নিজেই অ্যাক্সেস করে। সেক্ষেত্রে ডেকোরেটারকে ডাকা হলে ক্লাসটি সম্পূর্ণ হতে পারে না, যেহেতু ক্লাসটি অবশ্যই ডেকরেটারের ফলাফলের জন্য অপেক্ষা করে, যা শ্রেণীর অন্যতম বৈশিষ্ট্য হিসাবে সংরক্ষণ করা হবে।
u0b34a0f6ae

3

এখানে একটি সাধারণ উদাহরণ:

def mod_bar(cls):
    # returns modified class

    def decorate(fcn):
        # returns decorated function

        def new_fcn(self):
            print self.start_str
            print fcn(self)
            print self.end_str

        return new_fcn

    cls.bar = decorate(cls.bar)
    return cls

@mod_bar
class Test(object):
    def __init__(self):
        self.start_str = "starting dec"
        self.end_str = "ending dec" 

    def bar(self):
        return "bar"

আউটপুটটি হ'ল:

>>> import Test
>>> a = Test()
>>> a.bar()
starting dec
bar
ending dec

1

এটি একটি পুরানো প্রশ্ন তবে ভেনাসিয়ান জুড়ে এসেছিল। http://venusian.readthedocs.org/en/latest/

মনে হচ্ছে পদ্ধতিগুলি সাজাতে এবং এটি করার সময় আপনাকে ক্লাস এবং পদ্ধতি উভয়টিতে অ্যাক্সেস দেবে। দ্রষ্টব্য setattr(ob, wrapped.__name__, decorated)যে কলিংটি ভেনুসিয়ান ব্যবহারের সাধারণ উপায় নয় এবং কিছুটা উদ্দেশ্যকে পরাস্ত করে।

যেভাবেই হোক ... নীচের উদাহরণটি সম্পূর্ণ এবং এটি চালানো উচিত।

import sys
from functools import wraps
import venusian

def logged(wrapped):
    def callback(scanner, name, ob):
        @wraps(wrapped)
        def decorated(self, *args, **kwargs):
            print 'you called method', wrapped.__name__, 'on class', ob.__name__
            return wrapped(self, *args, **kwargs)
        print 'decorating', '%s.%s' % (ob.__name__, wrapped.__name__)
        setattr(ob, wrapped.__name__, decorated)
    venusian.attach(wrapped, callback)
    return wrapped

class Foo(object):
    @logged
    def bar(self):
        print 'bar'

scanner = venusian.Scanner()
scanner.scan(sys.modules[__name__])

if __name__ == '__main__':
    t = Foo()
    t.bar()

1

ফাংশনটি জানে না যে এটি সংজ্ঞা পয়েন্টে কোনও পদ্ধতি, কিনা যখন ডেকরেটার কোডটি চালায়। শ্রেণি / উদাহরণ শনাক্তকারীর মাধ্যমে যখন এটি অ্যাক্সেস করা হয় কেবল তখন এটির শ্রেণি / উদাহরণটি জানতে পারে। এই সীমাবদ্ধতা কাটিয়ে ওঠার জন্য, আপনি অ্যাক্সেস / কল করার সময় পর্যন্ত আসল সজ্জা কোডটি বিলম্ব করতে বর্ণনাকারী অবজেক্ট দ্বারা সজ্জিত করতে পারেন:

class decorated(object):
    def __init__(self, func, type_=None):
        self.func = func
        self.type = type_

    def __get__(self, obj, type_=None):
        func = self.func.__get__(obj, type_)
        print('accessed %s.%s' % (type_.__name__, func.__name__))
        return self.__class__(func, type_)

    def __call__(self, *args, **kwargs):
        name = '%s.%s' % (self.type.__name__, self.func.__name__)
        print('called %s with args=%s kwargs=%s' % (name, args, kwargs))
        return self.func(*args, **kwargs)

এটি আপনাকে স্বতন্ত্র (স্ট্যাটিক | শ্রেণি) পদ্ধতিগুলি সাজাতে দেয়:

class Foo(object):
    @decorated
    def foo(self, a, b):
        pass

    @decorated
    @staticmethod
    def bar(a, b):
        pass

    @decorated
    @classmethod
    def baz(cls, a, b):
        pass

class Bar(Foo):
    pass

এখন আপনি অন্তঃসংশোধনের জন্য ডেকোরেটর কোডটি ব্যবহার করতে পারেন ...

>>> Foo.foo
accessed Foo.foo
>>> Foo.bar
accessed Foo.bar
>>> Foo.baz
accessed Foo.baz
>>> Bar.foo
accessed Bar.foo
>>> Bar.bar
accessed Bar.bar
>>> Bar.baz
accessed Bar.baz

... এবং ফাংশন আচরণ পরিবর্তন করার জন্য:

>>> Foo().foo(1, 2)
accessed Foo.foo
called Foo.foo with args=(1, 2) kwargs={}
>>> Foo.bar(1, b='bcd')
accessed Foo.bar
called Foo.bar with args=(1,) kwargs={'b': 'bcd'}
>>> Bar.baz(a='abc', b='bcd')
accessed Bar.baz
called Bar.baz with args=() kwargs={'a': 'abc', 'b': 'bcd'}

দুঃখিতভাবে, এই পদ্ধতির বৈশিষ্ট্যগুলি সমতূল্য উইল McCutchen এর সমানভাবে বেমানান উত্তর । এই এবং উত্তরটি উভয়ই মূল প্রশ্নের দ্বারা প্রয়োজনীয় হিসাবে মেথড ডেকোরেশন সময়ের পরিবর্তে মেথড কল সময়ে কাঙ্ক্ষিত শ্রেণি অর্জন করে । পর্যাপ্ত প্রাথমিক সময়ে এই শ্রেণিটি অর্জনের একমাত্র যুক্তিযুক্ত উপায় হ'ল শ্রেণীর সংজ্ঞা সময়ে (যেমন, কোনও শ্রেণীর সাজসজ্জার বা মেটাক্লাসের মাধ্যমে) সমস্ত পদ্ধতির উপর আত্মবিশ্বাস করা। </sigh>
সিসিল কারি

1

যেমন অন্যান্য উত্তরগুলি নির্দেশ করেছে, ডেকোরেটার একটি ফাংশন-ইশ জিনিস, আপনি ক্লাসটি তৈরি করতে পারবেন না কারণ এই পদ্ধতিটি ক্লাসটি এখনও তৈরি হয়নি। তবে, ফাংশনটিকে "চিহ্নিত" করতে ডেকরেটার ব্যবহার করা এবং পরে পদ্ধতিটি মোকাবেলায় মেটাক্লাস কৌশলগুলি ব্যবহার করা পুরোপুরি ঠিক আছে, কারণ __new__পর্যায়ে ক্লাসটি তার মেটাক্লাস দ্বারা তৈরি করা হয়েছে।

এখানে একটি সহজ উদাহরণ:

আমরা @fieldপদ্ধতিটিকে একটি বিশেষ ক্ষেত্র হিসাবে চিহ্নিত করতে এবং এটি মেটাক্লাসে ডিল করতে ব্যবহার করি।

def field(fn):
    """Mark the method as an extra field"""
    fn.is_field = True
    return fn

class MetaEndpoint(type):
    def __new__(cls, name, bases, attrs):
        fields = {}
        for k, v in attrs.items():
            if inspect.isfunction(v) and getattr(k, "is_field", False):
                fields[k] = v
        for base in bases:
            if hasattr(base, "_fields"):
                fields.update(base._fields)
        attrs["_fields"] = fields

        return type.__new__(cls, name, bases, attrs)

class EndPoint(metaclass=MetaEndpoint):
    pass


# Usage

class MyEndPoint(EndPoint):
    @field
    def foo(self):
        return "bar"

e = MyEndPoint()
e._fields  # {"foo": ...}

আপনার এই লাইনে একটি টাইপ রয়েছে: পরিবর্তে if inspect.isfunction(v) and getattr(k, "is_field", False):এটি হওয়া উচিত getattr(v, "is_field", False)
এভিলটোশা

0

আপনার সাজসজ্জারের ফিরে আসা উচিত যে সাজানো পদ্ধতিতে পদ্ধতিটি ডাকা হচ্ছে সেই অবজেক্টটির শ্রেণিতে আপনার অ্যাক্সেস থাকবে। তাই ভালো:

def decorator(method):
    # do something that requires view's class
    def decorated(self, *args, **kwargs):
        print 'My class is %s' % self.__class__
        method(self, *args, **kwargs)
    return decorated

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

>>> obj = ModelA()
>>> obj.a_method()
My class is <class '__main__.ModelA'>

1
ধন্যবাদ তবে এই আমার সমাধানে আমি ঠিক সমাধানটি সমাধান করে যা আমার পক্ষে কাজ করে না। আমি সাজসজ্জার ব্যবহার করে একটি পর্যবেক্ষক প্যাটার্ন বাস্তবায়নের চেষ্টা করছি এবং পর্যবেক্ষণ প্রেরণকারীটিতে পদ্ধতিটি যুক্ত করার সময় যদি আমার কাছে কোনও পর্যায়ে ক্লাস না থাকে তবে আমি কখনই আমার পর্যবেক্ষণ প্রেরণকারী থেকে সঠিক প্রসঙ্গে পদ্ধতিটি কল করতে সক্ষম হব না। মেথড কলের উপর ক্লাস পাওয়া আমার প্রথমে পদ্ধতিটিকে সঠিকভাবে কল করতে সহায়তা করে না।
কার্ল জি

ওহো, আপনার পুরো প্রশ্নটি না পড়ায় আমার আলস্যের জন্য দুঃখিত
ম্যাককুচেন 23

0

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

import datetime as dt
import functools

def dec(arg1):
    class Timed(object):
        local_arg = arg1
        def __init__(self, f):
            functools.update_wrapper(self, f)
            self.func = f

        def __set_name__(self, owner, name):
            # doing something fancy with owner and name
            print('owner type', owner.my_type())
            print('my arg', self.local_arg)

        def __call__(self, *args, **kwargs):
            start = dt.datetime.now()
            ret = self.func(*args, **kwargs)
            time = dt.datetime.now() - start
            ret["time"] = time
            return ret
        
        def __get__(self, instance, owner):
            from functools import partial
            return partial(self.__call__, instance)
    return Timed

class Test(object):
    def __init__(self):
        super(Test, self).__init__()

    @classmethod
    def my_type(cls):
        return 'owner'

    @dec(arg1='a')
    def decorated(self, *args, **kwargs):
        print(self)
        print(args)
        print(kwargs)
        return dict()

    def call_deco(self):
        self.decorated("Hello", world="World")

@dec(arg1='a function')
def another(*args, **kwargs):
    print(args)
    print(kwargs)
    return dict()

if __name__ == "__main__":
    t = Test()
    ret = t.call_deco()
    another('Ni hao', world="shi jie")
    
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.