当包变得很大时,可能很难记住东西在哪里,并且我们想要的对象的点路径可能很麻烦。作者似乎解决这个问题的一种方法是将“最好的”对象的引用带到顶部,尽管这些代码实际上可以存在于下面的几个包级别中。
这让人可以说:
from pandas import wide_to_long
Run Code Online (Sandbox Code Playgroud)
代替
from pandas.core.reshape.melt import wide_to_long
Run Code Online (Sandbox Code Playgroud)
但是这样做的来龙去脉以及该方法的最佳实践是什么?不加载__init__.py带有许多导入的顶部(为了使它们在顶层可用)是否意味着包的单个对象的任何导入突然会占用比所需更多的内存 - 因为 中提到的所有内容都会自动__init__.py加载?
然而,包却可以做到这一点。例如,看看可以从顶层numpy或底层导入什么(可以在这个要点pandas中找到运行您自己的诊断的代码)。
$ python print_top_level_diagnosis.py numpy
--------- numpy ---------
599 objects can be imported from top level numpy:
19 modules
300 functions
104 types
depth count
0 162
1 406
2 2
3 29
4 1
$ python print_top_level_diagnosis.py pandas
--------- pandas ---------
115 objects can be imported from top level pandas:
12 modules
55 functions
40 …Run Code Online (Sandbox Code Playgroud) 该 GitHub 组织(到目前为止)拥有 27 个存储库。我希望能够立即查看所有这些内容,而不必逐一前往每个单独的存储库来查看其问题。
令我惊讶的是,我注意到在函数调用中,我可以用甚至不是有效的 python 标识符的dict字符串来解压 a 。
让我感到惊讶的是,因为参数名称必须是标识符,因此允许函数调用解包**kwargs具有非标识符的 a 且没有运行时错误,这似乎并不健康(因为它可能会将问题埋藏得比实际发生的地方更深)。
除非能够做到这一点有实际用途,在这种情况下我的问题就变成“那会有什么用途?”。
考虑这个函数:
def foo(**kwargs):
first_key, first_val = next(iter(kwargs.items()))
print(f"{first_key=}, {first_val=}")
return kwargs
Run Code Online (Sandbox Code Playgroud)
这表明,在函数调用中,您无法解压dict具有整数键的 a,这是预期的。
>>> t = foo(**{1: 2, 3: 4})
TypeError Traceback (most recent call last)
...
TypeError: foo() keywords must be strings
Run Code Online (Sandbox Code Playgroud)
另一方面,真正出乎意料且令人惊讶的是,您可以使用dict字符串键解压 a,即使这些不是有效的 python 标识符:
>>> t = foo(**{'not an identifier': 1, '12': 12, ',(*&$)': 100})
first_key='not an identifier', first_val=1
>>> t
{'not an identifier': 1, '12': 12, …Run Code Online (Sandbox Code Playgroud) 我可以ffmpeg通过这样做将 mp4 转换为 wav,使用,:
ffmpeg -vn test.wav -i test.mp4
Run Code Online (Sandbox Code Playgroud)
subprocess只要我的输入和输出是文件路径,我也可以使用它来做同样的事情。
但是如果我想ffmpeg直接在字节或“类文件”对象上使用io.BytesIO()怎么办?
这是一个尝试:
ffmpeg -vn test.wav -i test.mp4
Run Code Online (Sandbox Code Playgroud)
给我:
---------------------------------------------------------------------------
UnsupportedOperation Traceback (most recent call last)
<ipython-input-84-0ddce839ebc9> in <module>
5 with open('test.mp4', 'rb') as stream:
6 command = ['ffmpeg', '-i']
----> 7 proc = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=b)
...
1486 # Assuming file-like object
-> 1487 c2pwrite = stdout.fileno()
1488
1489 if stderr is None:
UnsupportedOperation: fileno
Run Code Online (Sandbox Code Playgroud)
当然,我可以使用临时文件来收集我的字节,但我希望能够避免写入磁盘(因为这一步只是转换管道中的一个链接)。
我想获得所有标准库包的名称列表。
通过这种方式,我的意思是那些我认为上市解析https://docs.python.org/3/library/:字符串name为其
__import__(name)
Run Code Online (Sandbox Code Playgroud)
sys.builtin_module_names 看起来很有希望,但事实并非如此。
我想过解析~/.pyenv/versions/3.8.6/lib/python3.8/或https://docs.python.org/3/library/,但肯定有更好的方法!
对于那些需要X 代表 Y 的人:我正在静态导航包的导入以分析它们——即,查看使用了哪些第三方包、哪些标准库、使用频率等。
在“组织”github帐户中,我可以设置一些全局秘密,PYPI_USERNAME并使PYPI_PASSWORD它们“可用于公共存储库”,然后在我的中使用它们,ci.yml如下所示:
twine upload dist/$PROJECT_NAME-$VERSION.tar.gz -u ${{ secrets.PYPI_USERNAME }} -p ${{ secrets.PYPI_PASSWORD }} --non-interactive --skip-existing --disable-progress-bar
Run Code Online (Sandbox Code Playgroud)
但在我的“用户”github 帐户中,我找不到任何此类组织范围内的秘密,因此我发现自己必须为每个单独的存储库输入我的秘密,这是反人类罪(现在)。
functools.partial我想知道这些和事实背后的故事 - 无论是声音设计还是继承的遗产 - inspect.signature(这里谈论 python 3.8)。
设置:
from functools import partial
from inspect import signature
def bar(a, b):
return a / b
Run Code Online (Sandbox Code Playgroud)
一切都从以下开始,这似乎符合咖喱标准。我们正在固定a位置3,a从签名中消失,它的值确实绑定到3:
f = partial(bar, 3)
assert str(signature(f)) == '(b)'
assert f(6) == 0.5 == f(b=6)
Run Code Online (Sandbox Code Playgroud)
如果我们尝试为 指定一个替代值a,f不会告诉我们我们得到了一个意外的关键字,而是告诉我们它有多个参数值a:
f = partial(bar, 3)
assert str(signature(f)) == '(b)'
assert f(6) == 0.5 == f(b=6)
Run Code Online (Sandbox Code Playgroud)
但现在如果我们b=3通过关键字修复,b则不会从签名中删除,它是对仅关键字的更改,我们仍然可以使用它(覆盖默认值,作为正常的默认值,这是我们在a之前无法做到的)案件):
f(a=2, …Run Code Online (Sandbox Code Playgroud) 我试图更好地理解 python 数据模型,但遇到了一些奇怪的事情。
def foo(a, b = 2):
return a / b
assert foo(20) == 10.0
# note: for sanity purposes, should also change signature, but not needed for effect
foo.__defaults__ = (10,)
assert foo(20) == 2.0
foo.__defaults__ = ()
foo.__kwdefaults__ = {'b': 10}
foo(20) # raises TypeError: foo() missing 1 required positional argument: 'b'
Run Code Online (Sandbox Code Playgroud)
预计会出现错误:__kwdefaults__仅针对关键字参数,因此让我们创建b一个仅关键字参数来尝试解决此问题:
from inspect import signature
foo.__signature__ = signature(lambda a, *, b=10: None)
foo(20) # still raises TypeError: foo() missing 1 required …Run Code Online (Sandbox Code Playgroud) 下面的函数确实(似乎)完成了这项工作,但似乎有比必要的更多的样板。
我确信有一种更优雅的方法。它将部分代码分解出来,使其看起来不像现在的复制/粘贴/编辑补丁工作。
但请注意,优雅并不是一切:我不希望性能受到影响。例如,我可以通过执行两个装饰器将代码减半:一个用于输入转换,另一个用于输出转换。但这会比当前版本效率低。
def input_output_decorator(preprocess=None, postprocess=None):
def decorator(func):
if inspect.ismethod(func):
if preprocess is not None:
if postprocess is not None: # both pre and post processes
@wraps(func)
def func_wrapper(self, *args, **kwargs):
return postprocess(func(self, preprocess(*args, **kwargs)))
else: # a preprocess but no postprocess
@wraps(func)
def func_wrapper(self, *args, **kwargs):
return func(self, preprocess(*args, **kwargs))
else: # no preprocess
if postprocess is not None: # no preprocess, but a postprocess
@wraps(func)
def func_wrapper(self, *args, **kwargs):
return postprocess(func(*args, **kwargs))
else: # no pre or post …Run Code Online (Sandbox Code Playgroud) 有许多方法装饰器的示例可以将方法转换为缓存的属性。但有时,我想检查缓存是否处于“活动”状态,这意味着该属性已被访问并且缓存已被填充。
例如,如果我使用rows缓存将 sql 表存储在 a 中rows,我想根据缓存计算表的长度(如果已填充),但如果没有填充,则通过单独的 sql 调用。如何在rows不触发访问的情况下检查是否已被访问?
这是一个很好的装饰器,取自 David Beazley 的“Python Cookbook”),我正在使用它来满足我的缓存属性需求。我已经增强了它以启用我当前的黑客。
class lazyprop:
def __init__(self, func):
self.func = func
def __get__(self, instance, cls):
if instance is None:
return self
else:
value = self.func(instance)
setattr(instance, self.func.__name__, value)
setattr(instance, self.func.__name__ + '__cache_active', True) # my hack
return value
Run Code Online (Sandbox Code Playgroud)
使用示例:
>>> class Test:
... def __init__(self, a):
... self.a = a
... @lazyprop
... def len(self):
... print('generating "len"')
... return len(self.a)
>>> t = …Run Code Online (Sandbox Code Playgroud) 从 Python 3.8 开始,functools有一个cached_property. 我一直在使用lazyprop基于 Beazley 的食谱(下面的代码)的类似装饰器,但是当我用内置函数替换时,我遇到了问题。这是其中之一。
当我在类定义中使用装饰器时,使用@运算符,它不会抱怨。
但是如果我将它与 一起使用setattr,我会得到:
TypeError: Cannot use cached_property instance without calling __set_name__ on it.
Run Code Online (Sandbox Code Playgroud)
Beazley 的简单版本虽然很好用。
TypeError: Cannot use cached_property instance without calling __set_name__ on it.
Run Code Online (Sandbox Code Playgroud) 假设我有一个包含一堆对象的模块,其中没有一个需要第三方库。好吧,几乎没有:一个函数(称为 )asterix()需要第三方包。asterix()不是该模块的重点,但在需要时使用很方便。
仅仅为了这个功能就必须要求并导入整个第三方包似乎是一种耻辱。我有什么选择?转移asterix()到另一个模块/包或供应商似乎很愚蠢。
我发现快速和干净的最佳平衡是简单地在asterix()其内部进行导入,可能需要一些额外的 try/ except 逻辑。它违反了最小意外原则和进口优先的惯例。但我告诉自己我永远都会违反一些事情。
群众智慧对此有何看法?我缺少什么明显的东西吗?
def asterix(...):
try:
import something_special
except ModuleNotFoundError as err:
# do something about err (help the user out!)
# The special code...
Run Code Online (Sandbox Code Playgroud)