Python函数式编程引用构造函数

wee*_*not 1 python constructor functional-programming

我想有一个ptr可以指向以下一的函数指针:

  • 一个功能,

  • 对象实例的方法,或

  • 对象的构造函数.

在后一种情况下,执行ptr()应该实例化该类.

def function(argument) :
    print("Function called with argument: "+str(argument))

class C(object) :
    def __init__(self,argument) :
        print("C's __init__ method called with argument: "+str(argument))

    def m(self,argument) :
        print("C's method 'm' called with argument: "+str(argument))

## works
ptr = function
ptr('A')

## works
instance = C('asdf')
ptr = instance.m
ptr('A')

## fails
constructorPtr = C.__init__
constructorPtr('A')
Run Code Online (Sandbox Code Playgroud)

这产生了输出:

Function called with argument: A

C's __init__ method called with argument: asdf

C's method 'm' called with argument: A

Traceback (most recent call last):   File "tmp.py", line 24, in <module>
    constructorPtr('A')

TypeError: unbound method __init__() must be called with C instance as first argument (got str instance instead)
Run Code Online (Sandbox Code Playgroud)

显示前两个ptr()调用有效,但最后一个没有.

aba*_*ert 5

这不起作用的原因是该__init__方法不是构造函数,它是初始化器.*

请注意,它的第一个参数是self- self必须__init__在调用其方法之前构造,否则,它将来自何处.

换句话说,它是一个普通的实例方法,就像instance.m是,但你试图把它称为未绑定的方法 - 就像你试图调用C.m而不是instance.m.


Python 确实有一个特殊的构造函数方法,__new__(虽然Python称之为"创建者",以避免与单阶段构造的语言混淆).这是一个静态方法,它将类构造为其第一个参数,将构造函数参数作为其他参数.您继承的默认实现object只是创建该类的实例并将参数传递给它的初始化程序.**所以:

constructor = C.__new__
constructor(C, 'A')
Run Code Online (Sandbox Code Playgroud)

或者,如果您愿意:

from functools import partial
constructor = partial(C.__new__, C)
constructor('A')
Run Code Online (Sandbox Code Playgroud)

但是,__new__除了子类之外,你想要直接调用它是非常罕见的__new__.类本身是可调用的,并且充当它们自己的构造函数 - 有效地意味着它们__new__使用适当的参数调用该方法,但是存在一些细微之处(并且在每种情况下它们不同,C()可能是您想要的,而不是C.__new__(C)).

所以:

constructor = C
constructor('A')
Run Code Online (Sandbox Code Playgroud)

正如user2357112在评论中指出:

一般来说,如果你想要在打电话时ptr这样做,你应该设置whatever_expression(foo)ptr(foo)ptr = whatever_expression

这是一个很好的,简单的经验法则,Python经过精心设计,可以尽可能地使用经验法则.


最后,作为附注,您可以分配ptr任何可调用的内容,而不仅仅是您描述的案例:

  • 一个功能,
  • 绑定方法(你的instance.m),
  • 构造函数(即一个类),
  • 一个未绑定的方法(例如,C.m你可以称之为罚款,但你必须instance作为第一个参数传递),
  • 一个绑定的类方法(例如,两者C.cminstance.cm,如果你定义cm为a @classmethod),
  • 一种未绑定的类方法(更难构造,更少用),
  • 一个静态方法(例如,都C.sminstance.sm,如果定义sm@staticmethod)
  • 各种特定于实现的"内置"类型,用于模拟函数,方法和类.
  • 带有__call__方法的任何类型的实例,

而事实上,所有的这些都是最后一个-的只是特例type类型有一个__call__方法,因为这样做types.FunctionTypetypes.MethodType,等等.


*如果您熟悉其他语言,如Smalltalk或Objective-C,您可能会因为Python 看起来不像是两阶段构造而被抛弃.在ObjC术语中,你很少实现alloc,但你总是把它称为:[[MyClass alloc] initWithArgument:a].在Python中,你可以假装这MyClass(a)意味着同样的事情(虽然它更像是[MyClass allocWithArgument:a],allocWithArgument:自动呼唤initWithArgument:你).

**实际上,这并不完全正确; 默认实现只返回一个实例C,并且Python自动调用__init__方法if isinstance(returnvalue, C).