代码之家  ›  专栏  ›  技术社区  ›  flow

在Python3.1中,如何在类构造过程中找到绑定方法的类?

  •  5
  • flow  · 技术社区  · 16 年前

    我想编写一个修饰器,使类的方法对其他方可见;但是,我描述的问题独立于这个细节。代码大致如下:

    def CLASS_WHERE_METHOD_IS_DEFINED( method ):
      ???
    
    def foobar( method ):
      print( CLASS_WHERE_METHOD_IS_DEFINED( method ) )
    
    class X:
    
      @foobar
      def f( self, x ):
        return x ** 2
    

    我的问题是,当装饰师 foobar() ,以查看该方法,但它还不可调用;相反,它可以查看该方法的未绑定版本。也许这可以通过在类上使用另一个修饰符来解决,该修饰符将处理必须对绑定方法执行的任何操作。接下来我要做的就是在修饰方法通过修饰器时用一个属性简单地指定它,然后使用类修饰器或元类进行后处理。如果我能做到这一点,那么我就不必解这个谜了,它仍然困扰着我:

    在上面的代码中,任何人都可以在下面填写有意义的行吗? CLASS_WHERE_METHOD_IS_DEFINED 这样装饰师就可以打印出 f 是被定义的,当它被定义的时候?或者在python 3中排除了这种可能性?

    3 回复  |  直到 7 年前
        1
  •  7
  •   Alex Martelli    16 年前

    当调用decorator时,使用函数作为参数调用它, 一个方法——因此,如果装饰器尽可能多地检查和内省它的方法,它将毫无用处,因为它只是一个函数,不包含关于封闭类的任何信息。我希望这能解决你的“谜语”,尽管是负面的!

    可能会尝试其他方法,例如对嵌套堆栈帧进行深入的自省,但这些方法非常简单、脆弱,而且肯定不会延续到pynie等python 3的其他实现中;因此,我强烈建议避免使用这些方法,以支持您已经在考虑的类装饰器解决方案,并且更清晰、更具Soli性。d.

        2
  •  0
  •   rmorshea    10 年前

    这是一篇很老的文章,但是反省不是解决这个问题的方法,因为反省可以更容易地用 metaclass 以及一些巧妙的类构造逻辑使用 descriptors .

    import types
    
    # a descriptor as a decorator
    class foobar(object):
    
        owned_by = None
    
        def __init__(self, func):
            self.func = func
    
        def __call__(self, *args, **kwargs):
            # a proxy for `func` that gets used when
            # `foobar` is referenced from by a class
            return self.func(*args, **kwargs)
    
        def __get__(self, inst, cls=None):
            if inst is not None:
                # return a bound method when `foobar`
                # is referenced from by an instance
                return types.MethodType(self.func, inst, cls)
            else:
                return self
    
        def init_self(self, name, cls):
            print("I am named '%s' and owned by %r" % (name, cls))
            self.named_as = name
            self.owned_by = cls
    
        def init_cls(self, cls):
            print("I exist in the mro of %r instances" % cls)
            # don't set `self.owned_by` here because 
            # this descriptor exists in the mro of
            # many classes, but is only owned by one.
            print('')
    

    使这项工作起作用的关键是元类——它搜索在它创建的要查找的类上定义的属性。 foobar 描述符。一旦这样做,它就通过描述符向它们传递有关它们所涉及的类的信息。 init_self init_cls 方法。

    自我自我 只为定义了描述符的类调用。这就是修改 福巴 应该生成,因为该方法只调用一次。当 因特尔CLSS 对所有可以访问修饰方法的类调用。这就是修改类的地方 福巴 可供参考的应作。

    import inspect
    
    class MetaX(type):
    
        def __init__(cls, name, bases, classdict):
            # The classdict contains all the attributes
            # defined on **this** class - no attribute in
            # the classdict is inherited from a parent.
            for k, v in classdict.items():
                if isinstance(v, foobar):
                    v.init_self(k, cls)
    
            # getmembers retrieves all attributes
            # including those inherited from parents
            for k, v in inspect.getmembers(cls):
                if isinstance(v, foobar):
                    v.init_cls(cls)
    

    例子

    # for compatibility
    import six
    
    class X(six.with_metaclass(MetaX, object)):
    
        def __init__(self):
            self.value = 1
    
        @foobar
        def f(self, x):
            return self.value + x**2
    
    class Y(X): pass
    
    # PRINTS:
    # I am named 'f' and owned by <class '__main__.X'>
    # I exist in the mro of <class '__main__.X'> instances
    
    # I exist in the mro of <class '__main__.Y'> instances
    
    print('CLASS CONSTRUCTION OVER\n')
    
    print(Y().f(3))
    # PRINTS:
    # 10
    
        3
  •  0
  •   tyrion    7 年前

    正如我在一些文章中提到的 other answers ,因为python 3.6,这个问题的解决方案非常简单,这要归功于 object.__set_name__ 使用正在定义的类对象调用。

    我们可以使用它来定义一个可以通过以下方式访问类的decorator:

    class class_decorator:
        def __init__(self, fn):
            self.fn = fn
    
        def __set_name__(self, owner, name):
            # do something with "owner" (i.e. the class)
            print(f"decorating {self.fn} and using {owner}")
    
            # 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)>