Python 装饰器与 CLOS "around"方法的比较

标签 python oop lisp decorator

对于这个抽象的问题,我回到了我的 CLOS(Common Lisp 对象系统)时代。

我正在增加问题以澄清:

在我看来,Python 装饰器有点像 CLOS 中的“around”方法。

据我所知,CLOS 中的“around”方法是一个方法/函数,它环绕同名的主要方法/函数。它也向上和向下遍历子类。这是一些语法(我刚拿起我的书)。

所有这些方法这将在一个类中:

(defmethod helloworld ()
  (format t "Hello World"))

也可以有 before 和 after 方法(为了完整起见,我将其放入):
(defmethod helloworld :before ()
  (format t "I'm executing before the primary-method"))

(defmethod helloworld :after ()
  (format t "I'm executing after the primary-method"))

最后是 around 方法(注意这里这个方法看起来像一个装饰器):
(defmethod helloworld :around ()
  (format t "I'm the most specific around method calling next method.")
  (call-next-method)
  (format t "I'm the most specific around method done calling next method."))

我相信输出将是:
I'm the most specific around method calling next method. 
I'm executing before the primary-method
Hello World
I'm executing after the primary-method
I'm the most specific around method done calling next method.

我一直将这种对类及其方法的理解作为开发代码的引用点。不幸的是,似乎很少有语言能在方法参数化和强大功能方面做到这一点。

我对 Python 很陌生,正在尝试了解装饰器是如何适应的。它们看起来有点松散,因为装饰器可以是一个完全外部的函数,它仍然能够在调用信息中操作信息,甚至修改实例和被调用对象的类变量,并且进一步说明它似乎执行了如图所示的 around 方法的作用。但我希望有人能帮助解释装饰器和周围方法之间的关系。我以为有人真的会喜欢这样做的机会。

使 CLOS 强大的原因在于,您可以使用这些方法进行多重继承。因此,一个类可以由包含处理自身的不同功能和属性的父类(super class)组成。因此,其中一个父类(super class)上的 around 方法可能会终止流程(如果“call-next-method”未完成),就像装饰器显然可以工作的方式一样。那么这与装饰器相同还是不同?在 around 方法中,您传递的是相同的参数,但对于装饰器,您传递的是严格定义中的“函数”,该定义会得到增强。但结果是一样的吗?

非常感谢!也许有人可以在 Python 中显示与上述内容的近似值。
完成调用 next 方法。

所以问题不在于在 Python 中实现 CLOS 方法,而是展示 Python 以 Pythonic 的方式接近该系统的程度。或者展示 Python 实际上比这更好。

这更像是我想到的那种例子:
class shape with attributes position and method area
class renderable with attribute visible and methods render, and render :around
class triangle with superclass shape and renderable attributes p1,p2,p3 and method render and method area
class square ...

如果三角形的实例具有visible=false,则render :around 将不会调用三角形的主要方法。

换句话说,render 方法的调用链是(a)renderable :around,(b)triangle primary,(c)finished renderable :around。如果triangle 有一个:after 方法,它会在primary 之后被调用,然后around 方法会结束。

我理解使用继承与考虑更新的设计模式的困难,但在这里我试图弥合我的 CLOS 知识。如果有一种设计模式匹配装饰器(比“装饰器”设计模式更准确),那也很好理解。

结论

我掌握了装饰器的窍门。但我想展示我在尝试模拟 CLOS 方法遍历时所处的位置。自从我拿到这本书以来,每个人都激励我去尝试它,而且我记得很清楚。感谢大家提出的所有好建议,它们都是拼图的一部分。就在单个装饰器中实现实际结构而言,Will 接近了,这就是通过动态方法查找插入它前进的方法(见下文)。我创建了一个装饰器,它可以完成我正在寻找的事情并且可以对任何类进行操作。我确信它可以更简洁,并且存在一个问题,即它只查找一个父类(super class)并且它在方法周围做的很奇怪,但它确实有效。
'''file: cw.py'''
'''This decorator does the job of implementing a CLOS method traversal through superclasses.  It is a very remedial example but it helped me understand the power of decorators.'''
'''Modified based on Richards comments'''
def closwrapper(func): # *args, **kwargs  ?
    def wrapper(self):  #what about superclass traversals???
        name = func.__name__
        # look for the methods of the class 
        before_func = getattr(self, name + "_before", None)
        after_func = getattr(self, name + "_after", None)
        around_func = getattr(self, name + "_around", None)
        sup = super(self.__class__,self)
        #self.__class__.__mro__[1]
        if sup:
            # look for the supermethods of the class (should be recursive)
            super_before_func = getattr(sup,name + "_before", None)
            super_after_func = getattr(sup,name + "_after", None))
            super_around_func = getattr(sup,name + "_around", None))

        ''' This is the wrapper function which upgrades the primary method with any other methods that were found above'''
        ''' The decorator looks up to the superclass for the functions.  Unfortunately, even if the superclass is decorated, it doesn't continue chaining up.  So that's a severe limitation of this implementation.'''
        def newfunc():
            gocontinue = True
            supercontinue = True
            if around_func: 
                gocontinue = around_func() 
                if gocontinue and super_around_func:
                  supercontinue = super_around_func()
            if gocontinue and supercontinue:
                if before_func: before_func()
                if super_before_func: super_before_func()
                result = func(self)
                if super_after_func: super_after_func()   
                if after_func: after_func()              
            else:
                result = None
            if gocontinue:
                if super_around_func: super_around_func(direction="out")
            if around_func: around_func(direction='out')
            return result
        return newfunc()

    return wrapper

# Really, the way to do this is to have the decorator end up decorating
# all the methods, the primary and the before and afters.  Now THAT would be a decorator!

class weeclass(object):

    @closwrapper
    def helloworld(self):
        print "Hello Wee World"

    def helloworld_before(self):
        print "Am I really so wee Before?  This method is not called on subclass but should be"



class baseclass(weeclass):
    fooey = 1

    def __init__(self):
        self.calls = 0

    @closwrapper
    def helloworld(self):
        print "Hello World"

    def helloworld_before(self):
        self.fooey += 2
        print "Baseclass Before"

    def helloworld_after(self):
        self.fooey += 2
        print "Baseclass After Fooey Now",self.fooey

    def helloworld_around(self,direction='in'):
        if direction=='in': 
            print "Aound Start"
            if self.fooey < 10:
                return True
            else:
                print ">>FOOEY IS TOO BIG!!!"
                self.fooey = -10
                return False
        #call-next-method
        if not direction=='in': 
            #self.barrey -= 4  #hello??  This should not work!!!  It should croak?
            print "Around End"  



class subclass(baseclass): 
    barrey = 2

    @closwrapper
    def helloworld(self):
        print "Hello Sub World Fooey",self.fooey,"barrey",self.barrey

    def helloworld_before(self):
        self.fooey -= 1
        self.barrey += 5
        print "  Sub Before"

    def helloworld_after(self):
        print "Sub After"

    def helloworld_around(self,direction='in'):
        if direction=='in': 
            print "Sub Around Start"
            if self.barrey > 4:
                print ">>Hey Barrey too big!"
                self.barrey -= 8
                return False
            else:
                return True
        #call-next-method
        if not direction=='in': 
            self.barrey -= 4
            print "Sub Around End"  

这是输出,因此您可以看到我正在尝试做什么。
Python 2.6.4 (r264:75706, Mar  1 2010, 12:29:19)  
[GCC 4.2.1 (Apple Inc. build 5646) (dot 1)] on darwin  
Type "help", "copyright", "credits" or "license" for more information.  
>>> import cw  
>>> s= cw.subclass()  
>>> s.helloworld()  
Sub Around Start  
Aound Start  
Sub Before  
Baseclass Before  
Hello Sub World Fooey 2 barrey 7  
Baseclass After Fooey Now 4  
Sub After  
Around End  
Sub Around End  
>>> s.helloworld()  
Sub Around Start
Aound Start  
Sub Before  
Baseclass Before  
Hello Sub World Fooey 5 barrey 8  
Baseclass After Fooey Now 7  
Sub After  
Around End  
Sub Around End  
>>> s.helloworld()  
Sub Around Start  
Aound Start  
Sub Before  
Baseclass Before  
Hello Sub World Fooey 8 barrey 9  
Baseclass After Fooey Now 10  
Sub After  
Around End  
Sub Around End  
>>> s.helloworld()  
Sub Around Start  
>>Hey Barrey too big!  
Sub Around End  
>>> s.helloworld()  
Sub Around Start  
Aound Start  
>>FOOEY IS TOO BIG!!!  
Around End  
Sub Around End  
>>> s.helloworld()  
Sub Around Start  
Aound Start  
Sub Before  
Baseclass Before  
Hello Sub World Fooey -9 barrey -6  
Baseclass After Fooey Now -7  
Sub After  
Around End  
Sub Around End  
>>> s.helloworld()  
Sub Around Start  
Aound Start  
Sub Before  
Baseclass Before  
Hello Sub World Fooey -6 barrey -5  
Baseclass After Fooey Now -4  
Sub After  
Around End  
Sub Around End  
>>> s.helloworld()  
Sub Around Start  
Aound Start  
Sub Before  
Baseclass Before  
Hello Sub World Fooey -3 barrey -4  
Baseclass After Fooey Now -1  
Sub After  
Around End  
Sub Around End  
>>> b = cw.baseclass()  
>>> b.helloworld()  
Aound Start  
Baseclass Before  
Am I really so wee Before?  This method is not called on subclass but should be  
Hello World  
Baseclass After Fooey Now 5  
Around End  
>>> b.helloworld()  
Aound Start  
Baseclass Before  
Am I really so wee Before?  This method is not called on subclass but should be  
Hello World  
Baseclass After Fooey Now 9  
Around End  
>>> b.helloworld()  
Aound Start  
Baseclass Before  
Am I really so wee Before?  This method is not called on subclass but should be  
Hello World  
Baseclass After Fooey Now 13  
Around End  
>>> b.helloworld()  
Aound Start  
>>FOOEY IS TOO BIG!!!  
Around End  
>>> b.helloworld()  
Aound Start  
Baseclass Before  
Am I really so wee Before?  This method is not called on subclass but should be  
Hello World  
Baseclass After Fooey Now -6  
Around End  

我希望这能让你对 CLOS 调用有一些了解,并激发关于如何改进装饰器的想法,或者如何抨击我什至试图这样做。 :-)

最佳答案

这是一个使用装饰器的快速而肮脏的实现,稍微好一点的实现(现在希望在正确的地方调用 around 方法)

def hints(before=None, after=None, around=None):
    """A decorator that implements function hints to be run before, after or
    around another function, sort of like in the CLOS."""

    # Make sure all of our hints are callable
    default = lambda *args, **kwargs: None
    before = before if callable(before) else default
    after = after if callable(after) else default
    around = around if callable(around) else default

    # The actual decorator function.  The "real" function to be called will be
    # pased to this as `fn`
    def decorator(fn):

        # The decorated function.  This is where the work is done.  The before
        # and around functions are called, then the "real" function is called
        # and its results are stored, then the around and after functions are
        # called.
        def decorated(*args, **kwargs):
            around(*args, **kwargs)
            before(*args, **kwargs)
            result = fn(*args, **kwargs)
            after(*args, **kwargs)
            around(*args, **kwargs)
            return result
        return decorated
    return decorator

# Shortcuts for defining just one kind of hint
def before(hint):
    return hints(before=hint)

def after(hint):
    return hints(after=hint)

def around(hint):
    return hints(around=hint)


# The actual functions to run before, after, around
def beforefn():
    print 'before'

def afterfn():
    print 'after'

def aroundfn():
    print 'around'


# The function around which the other functions should run
@before(beforefn)
@after(afterfn)
@around(aroundfn)
def fn():
    print 'Hello World!'

# Or use the single @hints decorator
@hints(before=beforefn, after=afterfn, around=aroundfn)
def fn2():
    print 'Goodbye World!'

调用 fn()结果如下:
>>> fn()
around
before
Hello World!
after
around
>>> fn2()
around
before
Goodbye World!
after
around

这种情况下的装饰器可能有点困惑,因为每个装饰器都涉及两个嵌套函数,而不是许多装饰器中看到的一个嵌套函数。

它可能不像 CLOS 版本那么优雅(我可能缺少它的一些功能),但它似乎可以做你想做的事。

关于Python 装饰器与 CLOS "around"方法的比较,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3672243/

相关文章:

python - 打印列表中每个项目的第一个字母

python - 如何在发送到远程数据库之前缓存数据?

使用点 (x,y) 访问 Python 2D 数组

lisp - 如何在 Scheme 的 eval 中使用外部变量?

python - 了解 PyTorch CNN channel

javascript - 在 ES2015 中定义一个类,构造函数方法是什么?为什么它是必不可少的?

c# - 如何在节点树中找到 NodeData?

functional-programming - 如何消除方案中此函数的可变性(N 皇后)

recursion - 递归减法不起作用

java - 在接口(interface)中使用默认方法是否违反了接口(interface)隔离原则?