你知道如何在Python中,如果v
是一个列表或一个字典,那么编写修改f
的函数(而不仅仅是返回新值)是很常见的。我想知道是否可以编写一个识别这些功能的检查器。
为简单起见,假设您有一个函数a
,它只接受一个参数 - v
并在有限时间内返回(返回值实际上是无关紧要的)。还假设对于任何输入值f(v)
,f
总是做同样的事情(即a
内部的逻辑不依赖于任何上下文或环境值 - 它是对{的纯粹计算{1}})。
是否可以编写一个函数m
,以便当m(f, v)
实际更改True
的原始值时,f(v)
会返回v
?
答案 0 :(得分:4)
没有;这相当于Halting problem。如果这样的m
确实存在,那么我可以写:
def f(a):
if m(f, a):
return a
else:
# Modify `a` somehow
return a
我们之间存在矛盾。
答案 1 :(得分:2)
如果您想检查该行为,可以使用原始值的deepcopy
编写简单的黑盒测试,例如:
def m(f, a):
original = copy.deepcopy(a)
f(a)
return original != a
def f(a):
a.append('a')
def k(a):
b = a
z = ['b']
m(f, z) # True
z = ['b']
m(k, z) # False
当然,如果参数是list
dict
,你必须深入复制并比较内部对象,但它是相同的逻辑
答案 2 :(得分:0)
非常简陋第一次出现在这只适用于iterables(并且没有:我不声称这解决了暂停问题或在一般情况下工作......):
from collections.abc import Sequence
def changes(lst):
lst.append(0)
def no_changes(lst):
return
def tries_to_change(f, v):
if isinstance(v, Sequence):
v_immutable = tuple(v)
try:
f(v_immutable)
return False
except AttributeError:
return True
print(tries_to_change(f=changes, v=[1, 2, 3])) # True
print(tries_to_change(f=no_changes, v=[1, 2, 3])) # False
这个想法是将输入转换为相同数据结构的不可变版本,看看会发生什么。非常粗糙!
并且在评论中提到 jbasko :这只会阻止元素的设置和删除;元素本身的修改(例如,如果参数是列表的列表;您仍然可以更改'内部'列表)将不会被检测到。
由于 PM 2Ring 的评论,小更新:如果列表包含可变的东西,这种方法不起作用(函数返回None
[这与停止问题一致答案...])。
def tries_to_change(f, v):
if isinstance(v, Sequence):
# check if the sequence contains immutable elements only:
try:
set(v)
except TypeError:
# no idea what could happen to the elements in the list...
return None
v_immutable = tuple(v)
try:
f(v_immutable)
return False
except AttributeError:
return True
答案 3 :(得分:0)
如果您知道 总是更改v
或从不更改v
,那么您可以做这样的事情:
class Checker(dict):
def __init__(self):
super().__init__()
self.changed = False
def __setitem__(self, index, value):
super().__setitem__(index, value)
self.changed = True
# implementing the rest of the mutating methods, e.g. `update`
# is left as an exercise for the reader
def m(f, v):
'''return True if f modifies v. Otherwise, return False'''
c = Checker()
c.update(v)
f(c)
return c.changed
你可能能够使用一些代码路径执行检查来查看是否所有路径都已执行,和/或做一些奇怪的AST黑客攻击以删除任何类型的条件......但是你&# 39; d还必须确保没有像这样的任何愚蠢的诡计:
def f(v):
'''Do terrible things in terrible ways.'''
q = v
if q.update({1:1}):
pass # because it will never hit here, but it *will* modify `v`
qux = [1,2]
qux.append({'derp': v})
qux[2]['derp'][42] = '...herring. A red one!'