跳转到主要内容

面向对象柯里化

项目描述

概述

Factory是一个面向对象的局部函数应用方法,也称为柯里化。Factory模块是这个模式的一个更强大的实现。一些改进包括

  • 更安全,因为无效参数会立即检测到,而不是在调用时

  • 智能支持类、实例方法以及所有其他可调用对象

  • 绑定参数可以作为属性检查和修改

  • 几个方便的参数绑定方法

  • 没有嵌套lambda的“俄罗斯套娃”

使用Factory可以

  • 简化回调函数的编写

  • 减少并发应用程序中的错误

  • 提供易于懒加载的评估

安装

Factory模块可以从Cheeseshop获取。源代码可在Google Code项目页面获取。

Factory模块可以像其他纯Python模块一样安装。支持Setuptools但不强制。您还可以将Factory.py文件直接包含在项目的源代码树中,但必须保留版权声明、版本和归属信息。

要在模块中运行测试,请在Factory/目录中执行以下命令

  • python doctest_Factory.py

  • nosetests test_Factory.py

关于柯里化

柯里化通过绑定原始函数的一些参数来从现有函数创建一个新函数

>>> def adder(x, y):
...     return x + y
>>> add_lambda = lambda y: adder(1, y)
>>> add_lambda(10)
11

截至Python 2.5,此模式内置了局部函数

>>> add_partial = functools.partial(adder, 1)
>>> add_partial(y=10)
11

工厂

工厂是柯里化模式更好的实现

>>> from Factory import *
>>> add_factory = Factory(adder, x=1)
>>> add_factory #doctest: +ELLIPSIS
<Factory(<function adder at ...>) at ...>
>>> add_factory(y=10)
11

与lambda和partial不同,工厂可以被检查和修改

>>> add_factory.x
1
>>> add_factory.x = 2
>>> add_factory(y=10)
12

可以检查将传递给函数的参数,这在调试时有时很有帮助

>>> import pprint
>>> args, kwargs = add_factory.generateArgs(y=10)
>>> pprint.pprint(kwargs)
{'x': 2, 'y': 10}
>>> args
[]

用法

在以下示例中,我们混合了FactoryMixin以在基类上提供factory类方法。

>>> class Foo(FactoryMixin):
...     def __init__(self, foo):
...         self.foo = foo
...
>>> foo_factory = Foo.factory()
>>> foo_factory.foo = 66

这相当于

>>> Factory(Foo) #doctest:+ELLIPSIS
<Factory(<class 'Foo'>) at ...>

使用mixin不是必需的,但它看起来不错且拼写更简单。

工厂有bind方法,可以同时设置多个属性并返回工厂。这在不需要将工厂分配给局部变量的情况下绑定参数非常有用。

>>> def doStuff(foo_factory):
...     return foo_factory.foo
>>> doStuff(foo_factory.bind(foo=11))
11
>>> foo_factory2 = foo_factory.bind(foo=42)
>>> foo_factory2 is foo_factory
True
>>> foo_factory.foo
42

您也可以在构建工厂时绑定属性

>>> foo_factory = Factory(Foo, foo=11)
>>> foo_factory.foo
11

工厂确保属性与参数匹配;这使得查找错误更容易(而不是在以后抛出意外的关键字参数

>>> foo_factory.bar = 42  #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'No such argument bar'

调用工厂时,参数会覆盖属性

>>> foo = foo_factory(foo=1111)
>>> foo.foo
1111

每次调用都返回一个新的实例

>>> foo2 = foo_factory()
>>> foo2 is foo
False

有效属性的集合是继承链中所有__init__参数的并集

>>> class Bar(Foo):
...     def __init__(self, bar, **kwargs):
...         super(Bar, self).__init__(**kwargs)
...         self.bar = bar
...
>>> bar_factory = Bar.factory()
>>> bar_factory.foo = 11
>>> bar_factory.bar = 42
>>> bar_factory.quux = 666  #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'No such argument quux'
>>> bar = bar_factory()
>>> bar.foo
11
>>> bar.bar
42

请确保传递给Factory的可调用对象(一个类,而不是一个实例)

>>> Factory(bar)  #doctest:+ELLIPSIS, +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: must provide known callable type, not <Factory.Bar object at ...>

当然,可调用对象是可行的

>>> class CallMe(object):
...     def __init__(self, x):
...         self.x = x
...     def __call__(self, y):
...         return self.x + y
>>> Factory(CallMe(1))(1)
2

可以将现有工厂作为新工厂的调用者传递。

>>> bar_factory = Bar.factory(bar=2)
>>> bar_factory2 = Factory(bar_factory, foo = 1)
>>> bar_factory is not bar_factory2
True
>>> bar_factory2.bar
2
>>> bar_factory2.bar = 4
>>> bar_factory.bar
2

与使用lambda不同,这不会创建嵌套的“套娃”

>>> bar_factory2.getCallable()
<class 'Bar'>

装饰器

returnFactory是一个装饰器,它用其生成的Factory等价物替换函数

>>> @returnFactory
... def mult(x, y):
...     return x * y
>>> fac = mult(x=10, y=5)
>>> isinstance(fac, Factory)
True
>>> fac()
50

factoryAttribute为装饰函数添加一个factory属性

>>> @factoryAttribute
... def adder(x, y):
...     return x + y
>>> fac = adder.factory(x=10)
>>> isinstance(fac, Factory)
True
>>> fac2 = adder.factory()
>>> fac is not fac2
True
>>> fac(y=42)
52

factoryDescriptor生成具有factory属性的实例方法。在类内部,使用此描述符而不是factoryAttribute。此类可以用作装饰器

>>> class Quux(object):
...     @factoryDescriptor
...     def doStuff(self, whatnot):
...          pass
>>> quux = Quux()
>>> fac = quux.doStuff.factory(whatnot=42)
>>> isinstance(fac, Factory)
True
>>> fac.whatnot
42

对象模板

对象模板是创建对象的模板。它们与工厂配合得很好。

Bunch只是一堆属性。Bunch的关键字参数被转换为属性

>>> b = Bunch(pants=42, shirt=15)
>>> b.pants
42
>>> b.shirt
15

调用Bunch返回一个新的副本

>>> c = b()
>>> c.__dict__ == b.__dict__
True
>>> c is b
False

当调用ObjectTemplate实例时,它将产生bunchClass的新实例。模板上的属性作为kwargs传递给bunch。但是,如果属性是可调用的,它将被调用,并使用返回值代替

>>> counter = itertools.count(1).next # an incrementing counter
>>> def color():
...     return "blue"
>>> template = ObjectTemplate(size=42,
...                           color=color,
...                           count=counter,
...                           bunchClass=Bunch)
>>> bunch = template()
>>> isinstance(bunch, Bunch)
True
>>> bunch.size
42
>>> bunch.color
'blue'
>>> bunch.count
1

每次调用模板都产生一个新的bunch。任何函数都将再次被调用

>>> bunch2 = template()
>>> bunch2.count
2

如果您想将可调用对象传递给bunch,请将其包装在lambda中

>>> template = ObjectTemplate()
>>> template.return_val = color
>>> template.a_function = lambda: color
>>> bunch = template()
>>> bunch.return_val
'blue'
>>> bunch.a_function #doctest:+ELLIPSIS
<function color at ...>

错误

错误、功能请求和赞誉可以直接发送给作者

项目详情


下载文件

下载您平台上的文件。如果您不确定选择哪个,请了解有关安装包的更多信息。

源分发

Factory-1.2.tar.gz (8.9 kB 查看哈希值)

支持者:

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误日志 StatusPage StatusPage 状态页面