2011-07-11 36 views
11

请考虑Python中的策略模式示例(从示例here改编而来)。在这种情况下,替代策略是一种功能。python方法如何自动接收'self'作为第一个参数?

class StrategyExample(object): 
    def __init__(self, strategy=None) : 
     if strategy: 
      self.execute = strategy 

    def execute(*args): 
     # I know that the first argument for a method 
     # must be 'self'. This is just for the sake of 
     # demonstration 
     print locals() 

#alternate strategy is a function 
def alt_strategy(*args): 
    print locals() 

以下是默认策略的结果。

>>> s0 = StrategyExample() 
>>> print s0 
<__main__.StrategyExample object at 0x100460d90> 
>>> s0.execute() 
{'args': (<__main__.StrategyExample object at 0x100460d90>,)} 

在上面的例子s0.execute是一种方法(未一个普通的功能),并且因此在args的第一个参数,如所预期,是self

以下是替代策略的结果。

>>> s1 = StrategyExample(alt_strategy) 
>>> s1.execute() 
{'args':()} 

在这种情况下是s1.execute一个普通的函数和作为预期的,不接收self。因此args是空的。等一下!这怎么发生的?

该方法和函数都以相同的方式调用。一个方法如何自动获得self作为第一个参数?当一种方法被普通的香草函数取代时,而不是如何得到self作为第一个参数?

我能找到的唯一区别是当我检查默认策略和替代策略的属性时。

>>> print dir(s0.execute) 
['__cmp__', '__func__', '__self__', ...] 
>>> print dir(s1.execute) 
# does not have __self__ attribute 

是否__self__属性对s0.execute(方法)上s1.execute(功能)的存在,但缺乏它在某种程度上解释这种行为差异?这一切如何在内部工作?

+1

你可以认为instance.method(ARG)'作为'InstanceClass.method的简写的'(实例,arg)'。 Python试图让事情尽可能简单明了,我发现这是一种“透明”的方式来访问实例,该实例称为函数 –

回答

3

您可以在“用户定义的方法”下的python参考中阅读完整说明here

如果您还有不明白的方法是如何工作的,一起来看看它的实现也许澄清事项:较短,更容易解释可以在method objects蟒蛇教程的描述中找到。当引用不是数据属性的实例属性时,将搜索其类。如果名称表示一个有效的类属性,它是一个函数对象,则通过打包(指向)实例对象和在抽象对象中一起找到的函数对象来创建方法对象:这是方法对象。当使用参数列表调用方法对象时,会从实例对象和参数列表构造一个新参数列表,并使用此新参数列表调用函数对象。

基本上,会发生什么在你的例子是这样的:

  • 分到一个班的功能(如当你声明的类体中的方法发生)是......的方法。
    • 当您通过该类访问该方法时,例如。 StrategyExample.execute你会得到一个“unbound method”:它不会“知道”它属于哪个实例,所以如果你想在实例上使用它,你需要自己提供实例作为第一个参数,例如。 StrategyExample.execute(s0)
    • 当您通过实例访问方法时,例如。 self.executes0.execute,你会得到一个“绑定方法”:它“知道”它“属于”哪个对象,并且将作为第一个参数被调用。
  • 然而,您直接分配给实例属性的功能却如self.execute = strategy甚至s0.execute = strategy是......只是一个普通的功能(相反的方法,它不通过类通)

为了让您的示例工作同在两种情况下:

  • 要么你把功能分为一个“真正”的方法:你可以用types.MethodType做到这一点:

    self.execute = types.MethodType(strategy, self, StrategyExample) 
    

    (你或多或少告诉类当execute被要求对这种特定情况下,应该把strategy到绑定的方法)

  • 或者 - 如果您的策略并不真的需要访问该实例 - 反过来将原始execute方法转换为静态方法(使其成为一个正常函数:它不会被调用与实例作为第一个参数,所以s0.execute()会做完全一样StrategyExample.execute()):

    @staticmethod 
    def execute(*args): 
        print locals() 
    
+0

非常明确的答案,并从Python教程太直了!表明我应该再次重新访问该教程,以吸收我刚刚浏览过的较深的语言细节。非常感谢你! –

1

当你做self.execute = strategy设置属性为纯方法:

>>> s = StrategyExample() 
>>> s.execute 
<bound method StrategyExample.execute of <__main__.StrategyExample object at 0x1dbbb50>> 
>>> s2 = StrategyExample(alt_strategy) 
>>> s2.execute 
<function alt_strategy at 0x1dc1848> 

绑定方法是调用传递一个实例作为第一个参数,除了通过所有参数的函数调用对象它被称为。

参见:Python: Bind an Unbound Method?

+0

术语是,您有一个未绑定的方法。如果你/绑定/方法(你可以做),它会再次隐式传递实例作为第一个参数。 – Arafangion

+0

感谢有关绑定未绑定方法的链接! –

6

的方法是用于该函数的包装,并调用与该实例作为第一个参数的函数。是的,它包含一个__self__属性(在3.x之前的Python中也是im_self),用于跟踪它所连接的实例。但是,将该属性添加到普通函数不会使其成为方法;你需要添加包装器。 Here is how(尽管你可能需要使用MethodTypetypes模块来获取构造函数,而不是使用type(some_obj.some_method)

功能包,顺便说一句,是通过该方法的__func__(或im_func)属性访问。

+0

非常有帮助的类比:“该方法是函数的包装...”。并感谢链接! –

7

您需要将一个未绑定的方法(即使用self参数)分配给对象的类或绑定方法。

经由descriptor mechanism,你可以让自己的绑定方法,这也是为什么当你指定(未绑定)功能的一类作品:

my_instance = MyClass()  
MyClass.my_method = my_method 

当调用my_instance.my_method(),查找将找不到输入my_instance,这就是为什么它在稍后的时间最终会这样做:MyClass.my_method.__get__(my_instance, MyClass) - 这是描述符协议。这将返回一个绑定到my_instance的新方法,然后在属性后使用()运算符执行该方法。

这将在MyClass的所有实例之间共享方法,无论它们何时创建。但是,在分配该属性之前,他们可能会“隐藏”该方法。

如果您只需要特定的对象有一个方法,只是手动创建一个绑定方法:

my_instance.my_method = my_method.__get__(my_instance, MyClass) 

有关的描述更详细(导向),看到here

+0

有趣的是,实际上向您显示的唯一答案是您可以制作绑定方法并尝试解释它,但没有得到一个单独的提示... – phant0m

+0

描述符访问是一种创建我不知道的方法的方法。我一直使用实例方法构造函数完成它。 – kindall

+0

@ kindall:你的意思是像你链接到的文章中显示的那样?而不是通过'type()'获得它,你可以导入它:'from types import MethodType' – phant0m

相关问题