隐藏Python模块

时间:2018-03-23 22:59:24

标签: python namespaces ipython jupyter-notebook

我在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_perfsha256_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避免污染全局命名空间的方法是什么?

5 个答案:

答案 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 _npimport 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.pysrc/data/data.py不是src/data/__init__.py,而是data.py。您的__init__.py就像以前一样,在from .data import sha256_perf, sha256_summary ,您有

import data

那就是它。现在当你sha256_perf获得包时,它的目录中包含sha256_summarydata.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名称。)