最近我开始玩Python,我遇到了一些特殊的闭包方式.请考虑以下代码:
adders=[0,1,2,3]
for i in [0,1,2,3]:
adders[i]=lambda a: i+a
print adders[1](3)
Run Code Online (Sandbox Code Playgroud)
它构建了一个简单的函数数组,它接受单个输入并返回由数字添加的输入.函数在for循环中构造,迭代器i从中循环0到3.对于这些数字中的每一个,lambda都会创建一个函数i,该函数捕获并将其添加到函数的输入中.最后一行将第二个lambda函数3作为参数调用.令我惊讶的是输出结果是6.
我期待一个4.我的理由是:在Python中,一切都是一个对象,因此每个变量都是指向它的指针.在创建lambda闭包时i,我希望它存储一个指向当前指向的整数对象的指针i.这意味着当i分配一个新的整数对象时,它不应该影响先前创建的闭包.遗憾的是,adders在调试器中检查数组表明它确实存在.所有的lambda功能指的最后一个值i,3,这将导致adders[1](3)返回6.
这让我想知道以下内容:
lambda函数以更改其值i时不会受到影响的方式捕获当前i值?在以下示例中:
def speak(volume):
def whisper(text):
print(text.lower() + ('.' * volume))
def yell(text):
print (text.upper() + ('!' * volume))
if volume > 1:
return yell
elif volume <= 1:
return whisper
func = speak(volume=10)
func('hello')
HELLO!!!!!!!!!! # <== obviously `10` is stored in `func` somewhere
Run Code Online (Sandbox Code Playgroud)
给定func,我将如何获得“体积”?func命名空间中是否有东西可以赋予值10?我以为也许会在其中func.__globals__,func.__dict__但两者都不会。
我一直在玩验尸调试,但遇到了一些问题.考虑以下pyton脚本example.py:
k = 0
print 1. / k
print 'continue ...'
Run Code Online (Sandbox Code Playgroud)
我可以用以下方式运行:
> python -m pdb example.py
Run Code Online (Sandbox Code Playgroud)
然后跳到第2行print 1. / k然后设置k = 1然后继续使用pdb命令c.
现在,如果我使用事后调试来执行此操作,则无法继续执行程序.我跑:
> python -i example.py
Run Code Online (Sandbox Code Playgroud)
然后在我进入shell之后我做了一个:
import pdb
pdb.pm()
Run Code Online (Sandbox Code Playgroud)
然后我可以k像以前一样更改值,但是我无法继续执行任何程序.Pdb只是退出.
我无法在任何地方发现它明确指出你无法在验尸中逐步完成一个程序.情况似乎如此.那么我想要了解验尸调试的价值.是错误发生时检查代码状态的唯一值吗?
当我在Python聊天室闲逛时,有人进入并报告了以下异常:
NameError: free variable 'var' referenced before assignment in enclosing scope
Run Code Online (Sandbox Code Playgroud)
我之前从未见过这个错误消息,并且用户只提供了一个不能自己引起错误的小代码片段,所以关闭我去google搜索信息,并且...似乎没有多少.在我搜索时,用户报告他们的问题已解决为"空白问题",然后离开了房间.
玩了一下后,我只能用这样的玩具代码重现异常:
def multiplier(n):
def multiply(x):
return x * n
del n
return multiply
Run Code Online (Sandbox Code Playgroud)
这给了我:
>>> triple = multiplier(3)
>>> triple(5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in multiply
NameError: free variable 'n' referenced before assignment in enclosing scope
Run Code Online (Sandbox Code Playgroud)
一切都很好,但我很难弄清楚这种异常如何在野外发生,因为上面我的例子是
......但鉴于我在本问题开头提到的报告,显然确实如此.
所以-怎么可以这个特定的例外发生在实际的代码?
在过去的几天里,我一直在努力了解Spark执行者如何知道在导入时如何使用给定名称的模块.我正在使用AWS EMR.情况:我通过键入来初始化EMR上的pyspark
pyspark - 主纱
然后,在pyspark,
import numpy as np ## notice the naming
def myfun(x):
n = np.random.rand(1)
return x*n
rdd = sc.parallelize([1,2,3,4], 2)
rdd.map(lambda x: myfun(x)).collect() ## works!
Run Code Online (Sandbox Code Playgroud)
我的理解是,当我输入numpy as np,主节点导入和识别的唯一节点numpy通过np.然而,与EMR簇(2个工作者节点),如果我上运行RDD地图功能,驱动程序发送功能的工作节点执行用于列表中每个项目的功能(对于每个分区),和一个返回成功结果.
我的问题是:工人们如何知道numpy应该作为np导入?每个worker都已经安装了numpy,但是我没有为每个节点明确定义一种导入模块的方法as np.
有关依赖关系的更多详细信息,请参阅Cloudera的以下帖子:http: //blog.cloudera.com/blog/2015/09/how-to-prepare-your-apache-hadoop-cluster-for-pyspark-jobs/
在Complex Dependency下,他们有一个示例(代码),其中pandas模块在每个节点上显式导入.
我听说过的一个理论是驱动程序分发在pyspark交互式shell中传递的所有代码.我对此持怀疑态度.我提出的反驳这个想法的例子是,如果在主节点上我输入:
print "hello"
Run Code Online (Sandbox Code Playgroud)
是每个工人节点还打印"你好"?我不这么认为.但也许我错了.
我对Eli Bendersky给出的这个例子感到有些惊讶(http://eli.thegreenplace.net/2015/the-scope-of-index-variables-in-pythons-for-loops/)
>>> def foo():
... lst = []
... for i in range(4):
... lst.append(lambda: i)
... print([f() for f in lst])
...
>>> foo()
[3, 3, 3, 3]
Run Code Online (Sandbox Code Playgroud)
但是当我想到它时,它有一定道理 - lambda正在捕捉对i的引用,而不是我的价值.
所以解决这个问题的方法如下:
>>> def foo():
... lst = []
... for i in range(4):
... lst.append((lambda a: lambda: a)(i))
... print([f() for f in lst])
...
>>> foo()
[0, 1, 2, 3]
Run Code Online (Sandbox Code Playgroud)
似乎这个工作的原因是,当我被提供给外部lambda时,外部lambda创建一个范围并取消引用i,将a设置为i.然后,返回的内部lambda保持对a的引用.
这是正确的解释吗?
我在lambda函数中遇到了一个重新定位的问题.我可以成功输出foo到stdout但是在使用max()包含a 时出现错误lambda- 请参阅下面的简化代码...
总而言之,我正在尝试budget在未知数量的一阶键中找到嵌套键的最大值.
(Pdb) foo = self.some_method() # some_method() returns a dict, printed in the next step
(Pdb) pp foo
{'1': {'count': 1,
'extra_data': {'activity-count': 1,
'budget': 0,
[...MORE KEY-VALUE PAIRS HERE...]
'version': 1},
[...LOTS MORE KEY-VALUE PAIRS HERE...]
'elements_total': defaultdict(<type 'int'>, {'result': 1, 'another_key': 2}),
'extra_year_data': defaultdict(<function <lambda> at 0x10e05bd70>, {})},
'2': {'count': 1,
'extra_data': {'activity-count': 1,
'budget': 3,
[...MORE KEY-VALUE PAIRS HERE...]
'version': 1},
[...LOTS MORE KEY-VALUE PAIRS HERE...] …Run Code Online (Sandbox Code Playgroud) 一般问题:如何访问函数闭包中的变量?
具体问题:如何cache从用 包装的 python 函数访问原始数据functools.lru_cache()?
如果我记住一个函数(示例取自文档)...
@lru_cache(maxsize=None)
def fib(n):
if n < 2:
return n
return fib(n-1) + fib(n-2)
>>> [fib(n) for n in range(16)]
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]
>>> fib.cache_info()
CacheInfo(hits=28, misses=16, maxsize=None, currsize=16)
Run Code Online (Sandbox Code Playgroud)
这里是cache定义的地方:https : //github.com/python/cpython/blob/f0851910eb8e711bf8f22165cb0df33bb27b09d6/Lib/functools.py#L491
当fib()被记忆时,向包装器lru_cache添加一个cache_info()和cache_clear()函数。 cache_clear()可以访问cache并且我可以访问,cache_clear()所以我可以以某种方式使用它来cache直接访问吗?
这是关于编程风格的问题:参数化函数的最"Pythonic"方式是什么(即使是正确的单词呢?)
假设我有一个函数(例如ODE求解器)接受两个参数的另一个函数(例如ODE本身)作为参数.
def solver(fun):
# Implementation goes here
# fun is a function handle that accepts two args e.g. fun(t,y)
Run Code Online (Sandbox Code Playgroud)
但是,我希望传递solver的函数由第三个值参数化
def myFun(t,y,A):
# Implementation goes here
Run Code Online (Sandbox Code Playgroud)
我一直在使用以下lambda函数处理这种情况:
A = 4
solution = solver(lambda t,y:myFun(t,y,A))
Run Code Online (Sandbox Code Playgroud)
我最近在网上看到一些帖子告诉我要避免lambda像瘟疫那样,而且Guido自己后悔允许这个功能.如果lambdas确实很糟糕,那么实施上述方法的"Pythonic"方法有哪些?没有lambda我遇到无法访问全局命名空间的问题,即我想这样做:
A = 4
def myFunNotLambda(t,y):
return myFun(t,y,A)
solution = solver(myFunNotLambda)
Run Code Online (Sandbox Code Playgroud)
但实现这一目标的唯一方法似乎是制作A全球,这绝对比使用更糟糕lambda
我试图在我的单元测试中使用 freezegun 来修补数据类中的字段,该字段设置为对象初始化时的当前日期。我想这个问题与任何修补被用作 default_factory 的函数的尝试有关,只是在 freezegun 之外。数据类被冻结,所以它是不可变的。
例如,如果我的数据类是:
@dataclass(frozen=True)
class MyClass:
name: str
timestamp: datetime.datetime = field(init=False, default_factory=datetime.datetime.now)
Run Code Online (Sandbox Code Playgroud)
当我使用 freezegun 修补 datetime 时,它对 MyClass 中时间戳的初始化没有影响(它仍然将时间戳设置为单元测试中 now() 返回的当前日期,导致测试失败)。
我假设它与在补丁到位之前加载的默认工厂和模块有关。我尝试修补日期时间,然后使用 importlib.reload 重新加载模块,但没有运气。
我目前的解决方案是:
@dataclass(frozen=True)
class MyClass:
name: str
timestamp: datetime.datetime = field(init=False)
def __post_init__(self):
object.__setattr__(self, "timestamp", datetime.datetime.now())
Run Code Online (Sandbox Code Playgroud)
哪个有效。
理想情况下,我想要一个非侵入性的解决方案,它不需要我更改我的生产代码来启用我的单元测试。
python ×10
lambda ×4
closures ×3
pdb ×2
python-3.x ×2
scope ×2
apache-spark ×1
caching ×1
debugging ×1
exception ×1
freezegun ×1
function ×1
nameerror ×1
patch ×1
pyspark ×1
python-2.7 ×1
unit-testing ×1