我正在回到我的CLOS(Common Lisp Object System)这个抽象问题的日子。
我正在扩充问题以澄清:
在我看来,Python装饰器有点像CLOS中的“around”方法。
根据我的记忆,CLOS中的“around”方法是一个包装同名主要方法/函数的方法/函数。 它也会遍历子类。这里有一些语法(我只是抓住了我的书)。
所有这些方法 这个将在一个类中:
(defmethod helloworld ()
(format t "Hello World"))
也可以在方法之前和之后(我为了完整而投入):
(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对我有用的是你可以使用这些方法进行多重继承。因此,一个类可以由超类组成,这些超类包含处理自身的不同功能和属性。因此,其中一个超类的around方法可能会终止flow(如果没有“call-next-method”),就像装饰器显然可以工作一样。那么它和装饰者一样,还是不同?在around方法中,你传递的是相同的参数,但是对于装饰器,你在一个严格的定义中传入“函数”,它会得到增强。但结果是否相同?
非常感谢!也许有人可以在Python中显示上述近似值。
完成调用下一个方法。
所以问题不是关于在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)可渲染的:around,(b)三角形primary,(c)完成可渲染:around。如果triangle有一个:after方法,它将在primary之后调用,然后around方法将完成。
我理解使用继承而不是考虑更新的设计模式的困难,但在这里我试图弥合我的CLOS知识。如果有一个与装饰器匹配的设计模式(比“装饰器”设计模式更准确),那么理解它也是很好的。
我正在掌握装饰器。但我想提出我试图模仿CLOS方法遍历的地方。自从我拿到这本书以来,每个人都激励我去尝试它,我记得很清楚。感谢所有伟大的建议,他们都是一个难题。在单个装饰器中实现实际结构方面,Will得到了接近,这就是通过动态方法查找推动它前进的方法(见下文)。我已经创建了一个装饰器,可以完成我正在寻找的任务,并且可以在任何类上运行。我确信它可能更干净,并且有一个问题,它只能查找一个超类,而且它正在做奇怪的方法,但确实有效。
'''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调用的理解,并激发关于如何改进该装饰器的想法,或者如何抨击我甚至尝试去做。 :-)
答案 0 :(得分:2)
这是一个快速和脏实现略微更好的实现(现在使用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版本那么优雅(我可能会错过它的一些功能),但它似乎可以做你想要的。
答案 1 :(得分:2)
你可以实现类似的东西。 Will是在正确的轨道上,但似乎“call-next-method”对于使用“around”非常关键,可以这样实现:
def around(callback):
def decorator(fn):
return lambda *a, **kw: callback(lambda: fn(*a, **kw))
return decorator
def hello_before(call_next_method):
print("I'm executing before the primary-method")
return call_next_method()
def hello_after(call_next_method):
value = call_next_method()
print("I'm executing after the primary-method")
return value
def hello_around(call_next_method):
print "I'm the most specific around method calling next method."
value = call_next_method()
print("I'm the most specific around method done calling next method.")
return value
@around(hello_around)
@around(hello_after)
@around(hello_before)
def helloworld():
print("Hello world")
helloworld()
这产生与您的输出完全相同的输出,具有相当类似的构造。只需注意你用它来装饰函数的顺序。
答案 2 :(得分:2)
受到原始问题和所有各种草稿的启发,我在辅助方法 Python模块周围/之前/之后实现了 CLOS-。
请参阅:http://code.activestate.com/recipes/577859-clos-like-aroundbeforeafter-auxiliary-methods/
我已经使用一些原生Python功能:
super()
内置函数,答案 3 :(得分:0)
我不确定我是否理解:around
,:before
和:after
,但是您正在寻找的是什么?
class Base(object):
def helloworld(self):
print('Hello World')
class After(object):
def helloworld(self):
super(After,self).helloworld()
print('After')
class Before(object):
def helloworld(self):
print('Before')
super(Before,self).helloworld()
class Around(object):
def helloworld(self):
print('Enter around')
super(Around,self).helloworld()
print('Exit around around')
class Foo(Around,Before,After,Base):
def helloworld(self):
super(Foo,self).helloworld()
foo=Foo()
这是foo
的MRO(method resolution order)。
print([cls.__name__ for cls in foo.__class__.mro()])
# ['Foo', 'Around', 'Before', 'After', 'Base', 'object']
当你说super(cls,self).helloworld()
时,Python
self
的MRO cls
helloworld
方法所以:
foo.helloworld()
产量
# Enter around
# Before
# Hello World
# After
# Exit around around
有关超级和MRO的更多信息,请参阅Shalabh Chaturvedi的excellent article。