2011-11-08 41 views
10

有时候,我发现我不得不使用功能名称很长,如os.path.abspathos.path.dirname一个很多在短短的几行代码。我不认为用这些函数乱扔全局名称空间是值得的,但是能够围绕我需要这些函数的线定义一个范围将是非常有帮助的。作为一个例子,这将是完美的:我可以在Python中的任何位置定义范围吗?

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

我认为“封闭”在这里不是合适的词。请参阅http://en.wikipedia.org/wiki/Closure_(computer_science)。也许“临时命名空间”是你的意思? –

+1

@RaymondHettinger - 我认为这个词是'范围'。 “我可以在Python中定义除函数,模块全局或内置之外的范围吗?” – Omnifarious

+0

通常,当你使用合理数量的代码行编码某个属于某个函数或方法的东西时。因此,“抛弃全局命名空间”应该不是问题,或者您的代码可能需要重新设计,而不是“范围”。 – MatthieuW

回答

19

Python没有像Lisp或计划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__列出你打算公开其姓名。在docs for the import statement中讨论了__all__的影响。

当然,你也可以通过在字典存储名字(虽然这个解决方案是不常见的)创建自己的命名空间:

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

只是做一个功能?

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() 
+0

这种方法有效,但它有一个显着的缺点,因为在使用这些函数时,您必须声明为* global *有用的任何东西。而且,在外部/内部嵌套示例中,您需要将Python 3的* nonlocal *关键字写入临时范围外的变量。 –

+0

@RaymondHettinger或者你可以将变量传递给函数。 –

+0

这对那些你想要写入的非临时变量有帮助吗? OP的“大量使用这些功能的15行”大概是用这些缩写功能来做的。 –

0

你可以在任何地方定义函数,调用它们,然后将其删除。但是没有办法可以使用可以使用语句的匿名函数。

-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. 

希望这有助于。

+0

在发布之前,您是否测试了它? 'with'不会在Python中创建一个本地作用域;这些名字仍然可以在'with'块之外找到。 – agf

+0

这没有用。抱歉。 –

+0

是的,好吧。很公平。你是对的,在我发布之前我没有完全测试过。抱歉。 –

1

简短答案是“否”。

Python有三个范围。它具有函数范围,全局(aka模块)范围和内置范围。你可以声明没有其他范围。

A 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在=符号的左侧看到一个变量,就会假定您正在创建一个新的局部变量。关键字globalnonlocal是一种说明您打算修改不在该函数范围内的变量的方式。

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: 
    ... 
+0

+1 try/finally是一个很好的接触:-) –

+0

@gnibbler我不确定要清楚地理解代码的动机。我认为这是使用'fuinally'子句总是执行的事实,它确保名称abspath和dirname在它们的定义和指令中的含义之后被删除。我对吗 ?如果是这样,我实际上并没有看到为什么这个代码会有相对于Raymond Hettinger代码中的类似代码片段的补充优点,只会删除名称以遵循定义和使用行。 – eyquem

+0

@eyquem,如果在同一范围内有另一个try/expect块,try/finally会确保变量不会泄漏,即使try/finally中存在异常 –

0

例外它不清楚,如果您更难以用写入识别表达式的长度,或者使用后在名称空间中留下剩余标识符的痕迹。

  • 对于第一,定义别名长期前缀,如由雷蒙德赫廷杰描述的技术中,是采用的人。

  • 对于第二个问题,我很惊讶没有人诉诸进口一个模块,在这个模块中,您认为这些指令和线路被认为是重物和乱抛垃圾。

顺便说一句,如果你通过os.path.abspathos.path.dirnames访问功能,这是不正确的说,功能(我想你的意思是他们的名字)垃圾的命名空间。由于它们属于模块osos。路径(取决于哪一个已导入),只有模块名称'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 

要看看它是如何工作的:在

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 

的片段执行后,该函数做()不存在了:

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) 

产生结果主模块,而是由做执行()现在躺在它没有名字的主模块的名称空间杂波创建的对象。此外,函数doing()内所需的所有标识符都是本地的。

所有需要的和需要的对象的创建可以委托给该模块heavy_code,不管他们有多少,而进口和执行功能做()只需要两行的主要模块,并功能做()heavy_code加上其呼叫线可以很容易地修改。

是不是模块设计用于什么?

相关问题