Ste*_*joa 1 python refactoring numpy decorator
请原谅我关于Python装饰器的另一个问题.我读了很多,但我想知道具体的以下问题的最佳解决方案是什么.
我写了几个函数,在numpy/scipy中做某种形式的梯度下降.给定矩阵X,我尝试迭代地最小化一些距离d(X,AS),作为A和S的函数.每个算法遵循相同的基本过程,但每个算法具有不同的更新规则.例如,这是我的两个函数(注意唯一的区别在于更新规则):
def algo1(X, A=None, S=None, K=2, maxiter=10, c=0.1):
M, N = X.shape
if A is None:
A = matrix(rand(M, K))
if S is None:
S = matrix(rand(K, N))
for iter in range(maxiter):
# Begin update rule.
A = multiply(A, (X*S.T + c)/(A*S*S.T + c))
S = multiply(S, (A.T*X + c)/(A.T*A*S + c))
# End update rule.
for k in range(K):
na = norm(A[:,k])
A[:,k] /= na
S[k,:] *= na
return A, S
Run Code Online (Sandbox Code Playgroud)
... 和另外一个:
def algo2(X, A=None, S=None, K=2, maxiter=10, c=0.1):
M, N = X.shape
O = matrix(ones([M, N]))
if A is None:
A = matrix(rand(M, K))
if S is None:
S = matrix(rand(K, N))
for iter in range(maxiter):
# Begin update rule.
A = multiply(A, ((X/(A*S))*S.T + c)/(O*S.T + c))
S = multiply(S, (A.T*(X/(A*S)) + c)/(A.T*O + c))
# End update rule.
for k in range(K):
na = norm(A[:,k])
A[:,k] /= na
S[k,:] *= na
return A, S
Run Code Online (Sandbox Code Playgroud)
这两个功能都是独立的.显然,这些函数要求重构.不同的代码单元是更新规则.所以这是我重构的尝试:
@iterate
def algo1(X, A=None, S=None, K=2, maxiter=10, c=0.1):
A = multiply(A, (X*S.T + c)/(A*S*S.T + c))
S = multiply(S, (A.T*X + c)/(A.T*A*S + c))
@iterate
def algo2(X, A=None, S=None, K=2, maxiter=10, c=0.1):
A = multiply(A, ((X/(A*S))*S.T + c)/(O*S.T + c))
S = multiply(S, (A.T*(X/(A*S)) + c)/(A.T*O + c))
Run Code Online (Sandbox Code Playgroud)
以下是一些潜在的函数调用:
A, S = algo1(X)
A, S = algo1(X, A0, S0, maxiter=50, c=0.2)
A, S = algo1(X, K=10, maxiter=40)
Run Code Online (Sandbox Code Playgroud)
问题:
iterate?让我特别困惑的是参数/参数,例如,使用与没有默认值,在装饰器和"包装器"中访问它们等.例如,更新规则本身不需要K,但初始化代码确实如此. ,我想知道我的功能签名是否正确.编辑:谢谢你的帮助.更多问题:
inner只有在传递参数时才需要包装器(例如)吗?因为我看到装饰器示例没有包装器,并且没有传递参数,并且它们工作得很好.functools看起来很有用; 它的主要目的是保留原始函数的元数据(例如,algo1.__name__和algo1.__doc__)?def algo1(X, A, S, c)和def inner(X, A=None, S=None, K=2, maxiter=10, c=0.1),呼叫algo1(X, maxiter=20)仍然有效.从语法上讲,我不确定为什么会这样.出于学习目的,您能否澄清(或引用参考资料)?谢谢!以下应该适合您想要使用的装饰器:
import functools
def iterate(update):
@functools.wraps(update)
def inner(X, A=None, S=None, K=2, maxiter=10, c=0.1):
M, N = X.shape
O = matrix(ones([M, N]))
if A is None:
A = matrix(rand(M, K))
if S is None:
S = matrix(rand(K, N))
for iter in range(maxiter):
A, S = update(X, A, S, K, maxiter, c)
for k in range(K):
na = norm(A[:,k])
A[:,k] /= na
S[k,:] *= na
return A, S
return inner
Run Code Online (Sandbox Code Playgroud)
正如您所注意到的,您可以简化algo1和algo2的签名,但它并不是真正的关键部分,并且保持签名完整可以简化您的测试和重构.如果你也想简化,你会改变的def语句那些,说,
def algo1(X, A, S, c):
Run Code Online (Sandbox Code Playgroud)
并且类似地简化了iterator装饰中的调用- 不需要两个参数,也不需要默认值.然而,避免这种简化部分实际上可以使你的生活更简单 - 如果装饰功能和装饰功能的结果保持完全相同的签名通常更简单,除非你有相反的真正特定需求.
编辑:OP不断回答这个问题...:
编辑:谢谢你的帮助.更多问题:
只有在传递参数时才需要包装器(例如内部)吗?因为我看到装饰器示例没有包装器,并且没有传递参数,并且它们工作得很好.
没有参数(在使用中)使用的装饰器@decorname被正在装饰的函数调用,并且必须返回一个函数; 与参数(例如@decorname(23))一起使用的装饰器必须返回一个("高阶")函数,而该函数又被正在修饰的函数调用,并且必须返回一个函数.被装饰的函数是否带参数,不会改变这组规则.技术上可以在没有内部函数的情况下实现这一点(我认为这是"包装器"的意思吗?)但是很少这样做.
通过阅读Python文档,functools看起来很有用; 是保持原始的功能(例如,algo1.的元数据,其主要目的名称和algo1.DOC)?
是的,functools.wraps完全用于此目的(functools也包含partial具有完全不同目的的).
随着签名
def algo1(X, A, S, c)和def inner(X, A=None, S=None, K=2, maxiter=10, c=0.1),呼叫algo1(X, maxiter=20)仍然有效.从语法上讲,我不确定为什么会这样.出于学习目的,您能否澄清(或引用参考资料)?谢谢!
这是因为inner实际上是用这些参数调用的函数(在algo1被修饰之后)并且只向下传递(到"真正的底层" algo1参数X, A, S, c(在包装algo1被给予简化签名的版本中).问题,如上所述,这使得元素(特别是签名)在被装饰的函数和最终的装饰函数之间不同;这对于读取和维护来说非常混乱,因此通常在两个级别保持相同的签名,除了特殊情况.