当您使用其他语言编写代码时,有时会创建一个块范围,如下所示:
statement
...
statement
{
statement
...
statement
}
statement
...
statement
一个目的(很多)是为了提高代码的可读性:显示某些语句构成一个逻辑单元,或者某些局部变量仅在该块中使用。
在Python中有同样的方式做同样的事情吗?
答案 0 :(得分:62)
不,没有语言支持来创建块范围。创建范围的唯一方法是函数,类或模块。
答案 1 :(得分:33)
Python中惯用的方法是保持你的功能简短。如果您认为需要这个,请重构您的代码! :)
Python为每个模块,类,函数,生成器表达式,字典理解,集合理解创建了一个新的范围,并且在Python 3.x中也为每个列表理解创建了一个新的范围。除此之外,函数内部没有嵌套的范围。
答案 2 :(得分:11)
通过在函数内声明一个函数然后立即调用它,您可以在Python中执行类似于C ++块作用域的操作。例如:
do_first_thing()
如果您不确定为什么要这样做,那么get the HTTP headers (and set, delete them)可能会说服您。
基本原则是尽可能紧密地确定所有内容的范围,而不引入任何“垃圾”。 (额外的类型/功能)扩展到比绝对需要更广的范围 - 没有其他人想要使用$(function() {
Highcharts.setOptions({
global: {
useUTC: false
}
});
// Create the chart
$('#container').highcharts('StockChart', {
chart: {
events: {
load: function() {
// set up the updating of the chart each second
var series = this.series[0],
hasPlotLine = false,
$button = $('#button'),
chart = $('#container').highcharts(),
yAxis = chart.yAxis[0],
plotLine,
d,
newY;
yAxis.addPlotLine({
value: 66,
color: 'red',
width: 2,
id: 'plot-line-1',
label: {
text: 66,
align: 'right',
y: newY,
x: 0
}
});
setInterval(function() {
var x = (new Date()).getTime(), // current time
y = Math.round(Math.random() * 100);
series.addPoint([x, y], true, true);
plotLine = yAxis.plotLinesAndBands[0].svgElem;
d = plotLine.d.split(' ');
newY = yAxis.toPixels(y) - d[2];
plotlabel = yAxis.plotLinesAndBands[0].label;
plotlabel.animate({
translateY: newY,
text: Highcharts.numberFormat(y, 2)
}, {
duration: 400,
step: function() {
$(this.element).html(Highcharts.numberFormat(this.textStr,2));
},
complete: function() { }
}),
plotLine.animate({
translateY: newY
}, 400);
}, 1000);
}
}
},
rangeSelector: {
buttons: [{
count: 1,
type: 'minute',
text: '1M'
}, {
count: 5,
type: 'minute',
text: '5M'
}, {
type: 'all',
text: 'All'
}],
inputEnabled: false,
selected: 0
},
title: {
text: 'Live random data'
},
yAxis: [{
opposite: false,
title: {
enabled: false
}
}],
exporting: {
enabled: false
},
series: [{
name: 'Random data',
data: (function() {
// generate an array of random data
var data = [],
time = (new Date()).getTime(),
i;
for (i = -999; i <= 0; i += 1) {
data.push([
time + i * 1000,
Math.round(Math.random() * 100)
]);
}
return data;
}())
}]
});
});
方法,所以它不应该在调用函数之外。
答案 3 :(得分:9)
我同意没有阻止范围。但是在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的陈述 创建范围的唯一方法是函数,类或模块 因为这看起来像另一个地方创建新范围。
答案 4 :(得分:1)
为了完整起见:您可以使用del结束局部变量的作用域。另请参阅When is del useful in Python?。不过,这当然不是惯用语。
statement
statement
# Begin block
a = ...
b = ...
statement
statement
del a, b
# End block
statement
答案 5 :(得分:1)
我想出了一个解决方案,它具有最简单的界面和最少的额外名称。
from scoping import scoping
a = 2
with scoping():
assert(2 == a)
a = 3
b = 4
scoping.keep('b')
assert(3 == a)
assert(2 == a)
assert(4 == b)
答案 6 :(得分:0)
模块(和包)是将程序划分为单独的命名空间的一种很棒的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禅”的怀疑者转变为信徒。