有时我发现我必须在几行代码中使用长名称的函数,例如os.path.abspath
和os.path.dirname
lot 。我不认为使用这些函数乱丢全局命名空间是值得的,但是能够在我需要这些函数的行周围定义范围将是非常有帮助的。举个例子,这将是完美的:
import os, sys
closure:
abspath = os.path.abspath
dirname = os.path.dirname
# 15 lines of heavy usage of those functions
# Can't access abspath or dirname here
我很想知道这是否可行
答案 0 :(得分:19)
Python在Lisp或Scheme中没有像 let 这样的临时名称空间工具。
Python中常用的技术是将名称放在当前命名空间中,然后在完成它们时将其取出。这种技术在标准库中大量使用:
abspath = os.path.abspath
dirname = os.path.dirname
# 15 lines of heavy usage of those functions
a = abspath(somepath)
d = dirname(somepath)
...
del abspath, dirname
减少打字工作量的另一种方法是缩短经常性前缀:
>>> import math as m
>>> m.sin(x / 2.0) + m.sin(x * m.pi)
>>> p = os.path
...
>>> a = p.abspath(somepath)
>>> d = p.dirname(somepath)
标准库中常用的另一种技术是不必担心污染模块命名空间,只需依靠 __ all __ 列出您打算公开的名称。 __ all __ 的效果在docs for the import statement中讨论。
当然,您也可以通过将名称存储在字典中来创建自己的命名空间(尽管此解决方案并不常见):
d = dict(abspath = os.path.abspath,
dirname = os.path.dirname)
...
a = d['abspath'](somepath)
d = d['dirname'](somepath)
最后,您可以将所有代码放在一个函数中(它有自己的本地命名空间),但这有许多缺点:
def temp(): # disadvantage 1: awkward setup global a, d # disadvantage 2: global declarations abspath = os.path.abspath dirname = os.path.dirname # 15 lines of heavy usage of those functions a = abspath(somepath) d = dirname(somepath) temp() # disadvantage 3: invoking the code
答案 1 :(得分:5)
这种做你想要的,但你必须重复名称
try:
abspath = os.path.abspath
dirname = os.path.dirname
# fifteen lines of code
finally:
del abspath
del dirname
如果在下面的情况中存在异常,这可以避免污染命名空间
try:
...
try:
abspath = os.path.abspath
dirname = os.path.dirname
# fifteen lines of code
finally:
del abspath
del dirname
... # don't want abspath or dirname in scope here even if there was
... # an exception in the above block
except:
...
答案 2 :(得分:2)
简短的回答是“不。”。
Python有三个范围。它具有功能范围,全局(aka模块)范围和内置范围。您不能声明其他范围。
class
声明看起来有点像作用域,但事实并非如此。它基本上是在一个对象上分配一堆字段的简写。该类中的函数无法访问这些字段而无需浏览它们所定义的对象。
这听起来比它更具限制性。在Python中,您还可以嵌套函数定义。嵌套函数定义获得对外部作用域的只读访问权限。这是“动态的”。在定义函数之前不必提及名称。这是一个例子:
def joe(x):
def bar():
return y
def baz(z):
y = x + 20
return x
y = x+5
return bar, baz
>>> a, b = joe(5)
>>> b(20)
5
>>> a()
10
因此,您可以通过定义一个嵌套函数来创建所需的值,使用它们并返回结果,从而在不牺牲太多局部性的情况下获得此效果。
我记得,在学习Python时,习惯于相当奇怪的范围规则是一个比较困难的部分。当引入嵌套函数时,在我看来,由于外部作用域的只读语义和闭包的动态作用域,它们使得作用域规则更加奇怪。
显然,在Python3中有一种使用nonlocal
关键字(类似于global
关键字)从封闭范围“导入”变量的方法,因此您可以在读/写中使用它上下文:
def joe(x):
def bar():
return y
def baz(z):
nonlocal y
y = x + 20
return x
y = x+5
return bar, baz
>>> a, b = joe(5)
>>> b(20)
5
>>> a()
25
否则,每当Python在=
符号的左侧看到变量时,它就会假设您正在创建一个新的局部变量。 global
和nonlocal
关键字是一种表明您打算修改不在函数范围内的变量的方法。
答案 3 :(得分:1)
刚做一个功能?
def do_things_with_those_functions():
abspath = os.path.abspath
dirname = os.path.dirname
# etc.
您可以在任何范围内执行此操作:
def outer_function():
a = 5
def inner_function():
print(a)
inner_function()
答案 4 :(得分:0)
您可以在任何地方定义函数,调用它们,然后删除它们。但是没有办法使用可以使用语句的匿名函数。
答案 5 :(得分:0)
目前尚不清楚你是否更容易被写入的识别表达式的长度所困扰,或者在使用它们后在名称空间中留下剩余标识符的痕迹。
首先,正如Raymond Hettinger所描述的那样,为长前缀定义别名的技术是第一个应用的技术。
对于第二种情况,我很惊讶没有人使用输入一个模块,在这个模块中,您认为重的和乱扔垃圾的指令和线路都是托运的。
顺便说一句,如果您通过os.path.abspath
和os.path.dirnames
访问函数,那么说这些函数(我想你的意思是他们的名字)就会丢失命名空间。由于它们属于模块 os 或 os.path (它取决于哪一个已导入),因此只有模块名称'os'命名空间中的或'os.path',以及内存中的模块对象,但不直接在命名空间中的函数名称。
因此,可以创建一个名为“heavy_code.py”的脚本:
def doing(x):
from os.path import abspath as a,dirname as d
## Execute all the desired processes and creations
def fufu(s,t):
return s+t
dedex = d[x]
#.........
#...........
#........
#............
#..........
## Return to the calling scope all that is needed there
return (dedex,fufu)
在主模块中,取一下gnibbler的答案:
one_path = 'I:/all/magala/zeru/kiol.py'
try:
from pp.bududu import doing
w,ff = doing(one_path)
finally:
del doing
要了解它的工作原理:
one_path = 'I:/all/magala/zeru/kiol.py'
try:
from pp.bududu.heavy_code import doing
print "in try : "
print dir()
print "executing doing()"
w,ff = doing(one_path)
print dir()
finally:
del doing
print "\nafter finally : "
print dir()
print '\nw ==',w
print 'ff(10,12) ==',ff(10,12)
产生结果:
in try :
['__builtins__', '__doc__', '__name__', '__package__', 'doing', 'one_path']
executing doing()
['__builtins__', '__doc__', '__name__', '__package__', 'doing', 'ff', 'one_path', 'w']
after finally :
['__builtins__', '__doc__', '__name__', '__package__', 'ff', 'one_path', 'w']
w == I:/all/magala/zeru
ff(10,12) == 22
执行代码段后,主模块中不再存在 doing()功能,而是执行执行()时创建的对象在主模块的命名空间中没有杂乱的名字。此外,函数 doing()中所需的所有标识符都是本地的。
创建所有需要和需要的对象可以委派给模块 heavy_code ,无论它们有多少,同时导入和执行函数 doing()需要主模块中只有两行, heavy_code 中的函数 doing()加上其调用行可以轻松修改。
这不是为什么设计的模块?
答案 6 :(得分:-3)
一般来说,打字并不是编写软件的难点,但如果你坚持:
class LocalNamespace(object):
def __enter__(self, *args, **kwargs):
pass
def __exit__(self, *args, **kwargs):
pass
with LocalNamespace() as foo:
abspath = os.path.abspath
dirname = os.path.dirname
# etc.
希望这有帮助。