我在Jupyter笔记本中有一个Python文件,src/data.py
,这意味着要读取数据文件并提供一些输出。
import numpy as np
import pandas as pd
sha256_perf = (
pd.read_csv('data/hashbench-output.txt', sep='\t', na_filter=False)
.query('Algorithm == "SHA256"')
)
mean_throughput = sha256_perf['Throughput (MiB/s)'].mean()
variance = sha256_perf['Error (± MiB/s)'] ** 2
total_variance = variance.sum()
row_count = sha256_perf.shape[0]
variance_of_mean = total_variance / (row_count ** 2)
error_of_mean = variance_of_mean ** 0.5
sha256_summary = pd.DataFrame(data=[[mean_throughput, error_of_mean]])
sha256_summary.columns = ['Mean Throughput (MiB/s)', 'Error (± MiB/s)']
其中,我唯一关心的变量是输出表 - sha256_perf
和sha256_summary
。但是,Python无法知道,所以如果我dir()
模块,我会得到所有东西:
>>> import src.data as data
>>> dir(data)
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__',
'__name__', '__package__', '__spec__', 'assumptions', 'error_of_mean',
'mean_throughput', 'np', 'pd', 'prd_scratch_2018', 'row_count', 'sha256_perf',
'sha256_summary', 'total_variance', 'util', 'variance', 'variance_of_mean']
如果这是Ruby或Scala,我可以在块中初始化sha256_summary
,例如:
sha256_summary = begin
mean_throughput = sha256_perf['Throughput (MiB/s)'].mean()
# ... etc. ...
df.columns = ['Mean Throughput (MiB/s)', 'Error (± MiB/s)']
df
end
即使在Java(8+)中,我也可以与Supplier
和lambda一起破解。
但据我所知,Python没有匿名块或多行lambda。所以到目前为止,我能够提出的最好的方法是将所有内容都放在一个函数中:
def create_summary():
mean_throughput = sha256_perf['Throughput (MiB/s)'].mean()
# ... etc. ...
sha256_summary.columns = ['Mean Throughput (MiB/s)', 'Error (± MiB/s)']
return sha256_summary
sha256_summary = create_summary()
但是这仍然导出create_summary
符号,我宁愿避免:
>>> dir(data)
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__',
'__name__', '__package__', '__spec__', 'assumptions', 'create_summary',
'np', 'pd', 'prd_scratch_2018', 'sha256_perf', 'sha256_summary', 'util']
Pythonic避免污染全局命名空间的方法是什么?
答案 0 :(得分:3)
您没有污染全局命名空间,因为您不关心的变量仍在data
命名空间内,也就是说,它们是data
模块的属性
此外,您不能只是隐藏模块属性:如果模块中定义了create_summary
,那么import data
会将data
带入其中create_summary
属性。
但是,您可以通过在del create_summary
末尾调用data.py
来删除它。
这样,它就不会出现在dir(data)
。
这是一个简短的例子:
# data.py
module_level_attribute = None
def init():
global module_level_attribute
module_level_attribute = 2
init()
del init
然后,module_level_attribute
将显示在dir(data)
中,但不会显示在init
中:
>>> import data
>>> dir(data)
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__',
'__name__', '__package__', '__spec__', 'module_level_attribute']
尽管如此,使用del
语句并不令人满意。
我更喜欢的方法是在函数中进行所有处理,并且只提供有趣的变量作为模块级属性,就像你一样。
没关系,因为它实际上并没有污染全局命名空间。
为了进一步发展,你可以用一个前导下划线来命名该初始化函数,以表明它将隐藏help
函数" private&#34 ;,尽管它仍然存在出现在dir(data)
。
但是,如果您确实只想导入所需内容,则应使用from ... import
语法。
这次,导入的变量将被带入全局命名空间,因此您将失去data.sha256_perf
的显式性。
另一方面,您从data
模块中获得了其他任何内容。
答案 1 :(得分:3)
Right leg's answer很好,但我想指出在模块中定义“有意义”名称的其他方法,而不是在事后删除内容。
第一个选项是在模块中定义__all__
。 __all__
是一系列字符串,是模块接口的一部分;如果您的模块的用户from modulename import *
,并且__all__
已定义,则他们只会在__all__
中提取名称。所以在你的情况下,你只需添加:
__all__ = ['sha256_perf', 'sha256_summary']
靠近模块的顶部(位置无关紧要,但按照惯例,它位于顶部附近,通常在导入之后)。许多内省工具也使用此信息来限制输出中包含模块的哪些部分(例如,help(modulename)
模块上__all__
排除了__all__
中未包含的类和函数
或者,您可以避免使用__all__
,并确保模块的所有非公共部分都使用前导下划线命名;如果未定义__all__
,则“虚拟”__all__
行为将包括不带前导下划线的名称,并省略带有前导下划线的名称。因此,如果除了您关注的两个名称之外的所有名称都是下划线前缀(包括导入的模块名称,例如import numpy as _np
,import pandas as _pd
),那么在使用from modulename import *
时它们将被省略,并且通常从help()
输出等中省略。
不,它不会更改dir
看到的名称集(虽然制表符完成功能会经常省略以下划线开头的名称),但这是声明被视为一部分的名称的官方方式你的公共API。
答案 2 :(得分:0)
如果您想要块初始化行为,只需重复使用变量名称作为函数名称。
def sha256_summary():
mean_throughput = sha256_perf['Throughput (MiB/s)'].mean()
# ... etc. ...
sha256_summary.columns = ['Mean Throughput (MiB/s)', 'Error (± MiB/s)']
return sha256_summary
sha256_summary = sha256_summary()
是的,这在Python中是允许的。功能只是另一种价值。这将在模块的全局变量中设置一个名为sha256_summary
的函数,然后以下赋值语句将用其返回值覆盖该名称。
Python的装饰者一直使用这种模式。它们通常返回包装函数替换,但实际上并不需要。装饰器可以返回任何值,此值将替换本地范围中的函数名称。所以我们可以将上面的模式实现为装饰器。
def begin(f):
return f()
然后,如果您像这样装饰该功能,
@begin
def sha256_summary():
mean_throughput = sha256_perf['Throughput (MiB/s)'].mean()
# ... etc. ...
sha256_summary.columns = ['Mean Throughput (MiB/s)', 'Error (± MiB/s)']
return sha256_summary
您再次获取sha256_summary
中的返回值,而不重复该名称。当然,begin
现在是模块中的全局。如果必须,之后删除它,但在Python中,模块包含的内容超出预期的导出是正常的。
答案 3 :(得分:0)
在函数中执行所有操作,调用该函数,然后删除该函数。声明要保留的任何内容global
。
def _private():
import numpy as np
import pandas as pd
global sha256_perf
sha256_perf = (
pd.read_csv('data/hashbench-output.txt', sep='\t', na_filter=False)
.query('Algorithm == "SHA256"')
)
mean_throughput = sha256_perf['Throughput (MiB/s)'].mean()
variance = sha256_perf['Error (± MiB/s)'] ** 2
total_variance = variance.sum()
row_count = sha256_perf.shape[0]
variance_of_mean = total_variance / (row_count ** 2)
error_of_mean = variance_of_mean ** 0.5
global sha_256_summary
sha256_summary = pd.DataFrame(data=[[mean_throughput, error_of_mean]])
sha256_summary.columns = ['Mean Throughput (MiB/s)', 'Error (± MiB/s)']
_private()
del _private
这种方法并不是特别Pythonic,但你总是在JavaScript中看到这种事情。
答案 4 :(得分:0)
使用包而不是模块来控制导出。 A"包"只是意味着您将模块文件放在一个目录中,然后使用一个名为__init__.py
的特殊Python文件导入您想要的名称。因此,src/data.py
和src/data/data.py
不是src/data/__init__.py
,而是data.py
。您的__init__.py
就像以前一样,在from .data import sha256_perf, sha256_summary
,您有
import data
那就是它。现在当你sha256_perf
获得包时,它的目录中包含sha256_summary
和data.py
,而data
中没有其他所有垃圾。 (它确实包含using ODate = oracle::occi::Date;
ODate ts;
ts.setDate(datetime.year(),datetime.month(),datetime.day(),datetime.hour (),datetime.minute(),datetime.second());
模块本身,以及每个Python模块附带的普通dunder名称。)