当您使用其他语言编写代码时,有时会创建一个块作用域,如下所示:
statement
...
statement
{
statement
...
statement
}
statement
...
statement
(很多)目的之一是提高代码的可读性:表明某些语句形成一个逻辑单元或某些局部变量仅在该块中使用。
在Python中是否有惯用的方式做同样的事情?
当您使用其他语言编写代码时,有时会创建一个块作用域,如下所示:
statement
...
statement
{
statement
...
statement
}
statement
...
statement
(很多)目的之一是提高代码的可读性:表明某些语句形成一个逻辑单元或某些局部变量仅在该块中使用。
在Python中是否有惯用的方式做同样的事情?
__exit__
和with
声明,更改,globals()
但失败了。
Answers:
Python中的惯用方式是使函数简短。如果您认为需要此代码,请重构代码!:)
Python为每个模块,类,函数,生成器表达式,dict理解,集合理解以及在Python 3.x中为每个列表理解创建新作用域。除此之外,函数内部没有嵌套作用域。
您可以通过在函数内部声明一个函数然后立即调用它来在Python中执行类似于C ++块作用域的操作。例如:
def my_func():
shared_variable = calculate_thing()
def do_first_thing():
... = shared_variable
do_first_thing()
def do_second_thing():
foo(shared_variable)
...
do_second_thing()
如果您不确定为什么要这样做,那么此视频可能会说服您。
基本原则是在不将任何“垃圾”(额外类型/函数)引入比绝对要求更广的范围的情况下,尽可能紧密地限制所有范围-do_first_thing()
例如,其他任何人都不想使用该方法,因此不应将其限制在范围之外。调用函数。
我同意没有限制范围。但是python 3中的一个地方使其像SEEM一样具有块作用域。
发生了什么,给了这个外观?这在python 2中正常工作,但是为了使变量泄漏在python 3中停止,他们做了这个技巧,并且此更改使其看起来像在这里具有块作用域。
让我解释。
根据作用域的思想,当我们在同一作用域内引入具有相同名称的变量时,应修改其值。
这就是python 2中发生的事情
>>> x = 'OLD'
>>> sample = [x for x in 'NEW']
>>> x
'W'
但是在python 3中,即使引入了具有相同名称的变量,它也不会被覆盖,但出于某种原因,列表理解的行为就像沙箱一样,并且似乎在其中创建了新的作用域。
>>> x = 'OLD'
>>> sample = [x for x in 'NEW']
>>> x
'OLD'
这与答案@Thomas的说法背道而驰。创建范围的唯一方法是函数,类或模块,因为这看起来就像在创建新范围的另一个地方。
模块(和包)是将程序划分为单独的命名空间的一种很棒的Python方式,这似乎是此问题的隐含目标。确实,当我学习Python的基础知识时,我对缺少块作用域功能感到沮丧。但是,一旦我理解了Python模块,就可以更优雅地实现我以前的目标,而无需块作用域。
作为激励并引导人们朝着正确的方向发展,我认为提供一些Python范围定义构造的明确示例很有用。首先,我解释了我使用Python类实现块范围的尝试失败。接下来,我将解释如何使用Python模块获得更多有用的东西。最后,我概述了包在加载和过滤数据中的实际应用。
有一会儿,我认为我已经通过将代码粘贴在类声明中来实现了块作用域:
x = 5
class BlockScopeAttempt:
x = 10
print(x) # Output: 10
print(x) # Output: 5
不幸的是,当定义一个函数时,这会崩溃:
x = 5
class BlockScopeAttempt:
x = 10
print(x) # Output: 10
def printx2():
print(x)
printx2() # Output: 5!!!
这是因为在类中定义的函数使用全局范围。解决此问题的最简单(虽然不是唯一)方法是显式指定该类:
x = 5
class BlockScopeAttempt:
x = 10
print(x) # Output: 10
def printx2():
print(BlockScopeAttempt.x) # Added class name
printx2() # Output: 10
这不是很优雅,因为必须根据函数是否包含在类中来编写不同的函数。
模块与静态类非常相似,但是根据我的经验,模块要干净得多。要对模块执行相同的操作my_module.py
,请在当前工作目录中创建一个文件,其内容如下:
x = 10
print(x) # (A)
def printx():
global x
print(x) # (B)
然后在我的主文件或交互式(例如Jupyter)会话中,
x = 5
import my_module # Output: 10 from (A)
my_module.printx() # Output: 10 from (B)
print(x) # Output: 5
作为解释,每个Python文件定义一个模块,该模块具有自己的全局名称空间。导入模块后,您可以使用以下命令访问此命名空间中的变量.
语法。
如果要在交互式会话中使用模块,则可以在开始时执行这两行
%load_ext autoreload
%autoreload 2
修改相应文件后,模块会自动重新加载。
包的概念是模块概念的略微扩展。软件包是一个包含(可能为空白)__init__.py
文件的目录,该文件在导入时执行。可以使用.
语法访问此目录中的模块/软件包。
对于数据分析,我经常需要读取一个大数据文件,然后以交互方式应用各种过滤器。读取文件需要几分钟,所以我只想做一次。基于我在学校中学到的有关面向对象编程的知识,我曾经认为应该编写用于过滤和加载的代码作为类中的方法。这种方法的主要缺点是,如果我随后重新定义过滤器,则类的定义会更改,因此必须重新加载整个类,包括数据。
如今,使用Python,我定义了一个名为的包my_data
,其中包含名为load
和的子模块filter
。在filter.py
我里面可以做一个相对导入:
from .load import raw_data
如果我修改filter.py
,autoreload
则将检测到更改。它不会重新加载load.py
,所以我不需要重新加载数据。这样,我可以在Jupyter笔记本中创建过滤代码的原型,将其包装为一个函数,然后从笔记本中直接剪切粘贴到中filter.py
。弄清楚这一点,彻底改变了我的工作流程,使我从“ Python禅”的怀疑者转变为信徒。
One purpose (of many) is to improve code readability
-正确编写(即遵循zen的python)的Python代码将不需要这种修饰即可读取。实际上,这是我喜欢Python的(许多)事情之一。