Class 方法组合
Class method composition
我想通过链接它们的方法来组合函数。
我的目标是实现类似 collect(10, mul(3).div(2).add(10))
的目标,它应该做到 add(10)(div(2)(mul(3)*10))
,结果是 25.0
显然这只是一个玩具示例,表明我想通过将它们链接在一起来组合不同的方法。
我创建了一个 class Expr
来处理不同的方法。
但是,我能够实现此目的的唯一方法是使用 全局变量 container
来存储调用的方法,如下所述:
问题:除了全局容器之外,是否还有另一种(更好的)方法来以某种方式存储被调用的方法?
Edited: added def compose(...)
container = []
class Expr:
def __init__(self, f):
self.f = f
global container
container.append(f)
@staticmethod
def add(y):
return Expr(lambda x: x + y)
@staticmethod
def mul(y):
return Expr(lambda x: x*y)
@staticmethod
def sub(y):
return Expr(lambda x: x - y)
@staticmethod
def div(y):
return Expr(lambda x: x/y)
# Dummy function to be able to start without `Expr.mul(...)`
def mul(y):
return Expr.mul(y)
# Handles composition of functions
def composer(*functions):
def inner_func(arg):
for f in reversed(functions):
arg = f(arg)
return arg
return inner_func
def collect(el, comp_funcs: Expr):
global container
try:
f = composer(*reversed(container)) # create composed method
container = [] # delete container elements
return f(el) # call composed function
except:
print("Upps, something went wrong")
container = []
collect(10, mul(3).div(2).add(10))
# Result is 25.0
感谢您的帮助!
这几天我一直在想这个问题。
你绝对不应该使用 global
。简单的方法是将整个东西放在另一个 class 中,然后将 container
作为实例变量访问。
但我认为您错过了一个微不足道的方法。这类似于您所做的 class 必须预定义它希望能够链接的每个特定方法。
class Foo:
def __init__(self, n):
self._n = n
@property
def data(self):
return self._n
def add(self, x):
self._n += x
return self
def div(self, x):
self._n /= x
return self
print(repr(Foo(10).add(2).div(2).data))
# 6.0
我不喜欢这个,因为它不是很通用;它在副作用的基础上运作;你需要从一开始就定义好所有内容。
所以这是一个更通用的设置,其中您有一个 Composer
来管理您可以链接的函数,还有一个 Chainable
在两种状态之间交替:
- 存储“当前值”和下一个函数
- 存储上一个函数的结果
作曲家内置了一个装饰器,允许您稍后注册其他通用函数。
from typing import *
from dataclasses import dataclass
T = TypeVar("T") # the initial data
Func = Callable[[T], T] # the function that transforms the data
RegisteredFunc = Callable[[Any], Func] # the function that returns the function that transforms the data
class Composer:
functions: Dict[str, RegisteredFunc]
def __init__(self):
self.functions = {}
def register(self, func: RegisteredFunc) -> RegisteredFunc:
self.functions[func.__name__] = func
return func
@dataclass
class Chainable:
data: T
fun: Optional[RegisteredFunc] = None
composer: Optional["Composer"] = None
def __getattr__(self, item) -> "Chainable":
return Chainable(self.data, self.composer.functions[item], self.composer)
def __call__(self, *args, **kwargs) -> "Chainable":
next_data = self.fun(*args, **kwargs)(self.data) if self.fun else self.data
return Chainable(next_data, composer=self.composer)
def __str__(self):
return str(self.data)
def __repr__(self):
return repr(self.data)
comp = Composer()
@comp.register
def add(x) -> Func:
return lambda n: n + x
@comp.register
def div(x) -> Func:
return lambda n: n / x
print(repr(Chainable(10, composer=comp).add(2).div(2)))
# 6.0
print(repr(Chainable(1, composer=comp).add(2).add(1).add(2).div(4)))
# 1.5
我想通过链接它们的方法来组合函数。
我的目标是实现类似 collect(10, mul(3).div(2).add(10))
的目标,它应该做到 add(10)(div(2)(mul(3)*10))
,结果是 25.0
显然这只是一个玩具示例,表明我想通过将它们链接在一起来组合不同的方法。
我创建了一个 class Expr
来处理不同的方法。
但是,我能够实现此目的的唯一方法是使用 全局变量 container
来存储调用的方法,如下所述:
问题:除了全局容器之外,是否还有另一种(更好的)方法来以某种方式存储被调用的方法?
Edited: added
def compose(...)
container = []
class Expr:
def __init__(self, f):
self.f = f
global container
container.append(f)
@staticmethod
def add(y):
return Expr(lambda x: x + y)
@staticmethod
def mul(y):
return Expr(lambda x: x*y)
@staticmethod
def sub(y):
return Expr(lambda x: x - y)
@staticmethod
def div(y):
return Expr(lambda x: x/y)
# Dummy function to be able to start without `Expr.mul(...)`
def mul(y):
return Expr.mul(y)
# Handles composition of functions
def composer(*functions):
def inner_func(arg):
for f in reversed(functions):
arg = f(arg)
return arg
return inner_func
def collect(el, comp_funcs: Expr):
global container
try:
f = composer(*reversed(container)) # create composed method
container = [] # delete container elements
return f(el) # call composed function
except:
print("Upps, something went wrong")
container = []
collect(10, mul(3).div(2).add(10))
# Result is 25.0
感谢您的帮助!
这几天我一直在想这个问题。
你绝对不应该使用 global
。简单的方法是将整个东西放在另一个 class 中,然后将 container
作为实例变量访问。
但我认为您错过了一个微不足道的方法。这类似于您所做的 class 必须预定义它希望能够链接的每个特定方法。
class Foo:
def __init__(self, n):
self._n = n
@property
def data(self):
return self._n
def add(self, x):
self._n += x
return self
def div(self, x):
self._n /= x
return self
print(repr(Foo(10).add(2).div(2).data))
# 6.0
我不喜欢这个,因为它不是很通用;它在副作用的基础上运作;你需要从一开始就定义好所有内容。
所以这是一个更通用的设置,其中您有一个 Composer
来管理您可以链接的函数,还有一个 Chainable
在两种状态之间交替:
- 存储“当前值”和下一个函数
- 存储上一个函数的结果
作曲家内置了一个装饰器,允许您稍后注册其他通用函数。
from typing import *
from dataclasses import dataclass
T = TypeVar("T") # the initial data
Func = Callable[[T], T] # the function that transforms the data
RegisteredFunc = Callable[[Any], Func] # the function that returns the function that transforms the data
class Composer:
functions: Dict[str, RegisteredFunc]
def __init__(self):
self.functions = {}
def register(self, func: RegisteredFunc) -> RegisteredFunc:
self.functions[func.__name__] = func
return func
@dataclass
class Chainable:
data: T
fun: Optional[RegisteredFunc] = None
composer: Optional["Composer"] = None
def __getattr__(self, item) -> "Chainable":
return Chainable(self.data, self.composer.functions[item], self.composer)
def __call__(self, *args, **kwargs) -> "Chainable":
next_data = self.fun(*args, **kwargs)(self.data) if self.fun else self.data
return Chainable(next_data, composer=self.composer)
def __str__(self):
return str(self.data)
def __repr__(self):
return repr(self.data)
comp = Composer()
@comp.register
def add(x) -> Func:
return lambda n: n + x
@comp.register
def div(x) -> Func:
return lambda n: n / x
print(repr(Chainable(10, composer=comp).add(2).div(2)))
# 6.0
print(repr(Chainable(1, composer=comp).add(2).add(1).add(2).div(4)))
# 1.5