Python:在单个文件中获取函数和所有依赖项的好方法?

Pet*_*ter 6 python automated-refactoring pycharm

我正在研究一个可以增长,增长和发展的庞大的Python代码库.这不是一个单一的应用程序 - 更多的是一些共享一些常见代码的实验.

我经常要公开发布一个给定的实验.我不想发布我的整个糟糕的代码库,只是发布运行给定实验所需的部分.所以基本上我想要抓取所有导入并将所调用的任何函数(或至少导入的所有模块)复制到单个文件中,我可以将其作为演示版本发布.我当然只想对当前项目中定义的文件执行此操作(不是像numpy这样的依赖包).

我现在正在使用PyCharm,并且无法找到该功能.有没有这样做的工具?

编辑:我创建了公共发布包来解决这个问题.给定一个主模块,它会遍历依赖模块并将它们复制到一个新的仓库中.

Pet*_*ter 6

所以最后,为了解决我们的问题,我做了一个叫做public-release的工具,它收集你想要发布的模块的所有依赖,把它们扔到一个单独的 repo 和安装脚本等等,这样你的代码就可以稍后轻松运行。


jpm*_*c26 5

将所有代码卡在一个模块中不是一个好主意。一个很好的示例原因就是当您的一个实验依赖于两个具有相同函数名的不同定义的模块时。使用单独的模块,您的代码很容易区分它们;要将它们填充在同一个模块中,编辑器将不得不进行一些hacky函数的重命名(例如,在它们之前加上旧的模块名称或其他名称),如果模块中的某些其他函数调用该函数,情况将变得更糟。名称冲突。您实际上必须完全替换模块作用域机制才能执行此操作。

建立模块依赖关系列表也是一项艰巨的任务。考虑进行一个依赖于依赖numpy的模块的实验。您几乎肯定会希望最终用户实际安装numpy软件包而不是捆绑它,因此现在编辑器必须具有某种方法来区分要包括的模块和希望以其他方式安装的模块。最重要的是,您必须考虑类似函数何时按行导入模块(而不是模块顶部)和其他异常情况的事情。

您要的编辑器太多了。您确实有两个问题:

  1. 将实验代码与发布准备好的代码分开。
  2. 打包您的稳定代码。

分离实验代码

源代码控制是您第一个问题的答案。这将允许您在本地计算机上创建所需的任何实验代码,并且只要不提交它,就不会使用实验代码来污染代码库。如果您确实想提交此代码用于备份,跟踪或共享,则可以在此处使用分支。将分支标识为稳定分支(通常是SVN中的主干,而git中的master),并且仅将实验代码提交给其他分支。然后,当实验功能分支变得足够成熟以至于可以发布时,您可以将它们合并到稳定分支中。这样的分支设置具有额外的好处,即允许您根据需要将实验彼此隔离。

服务器托管的源代码控制系统通常会使事情变得更加简单和安全,但是如果您是唯一的开发人员,则仍然可以在没有服务器的情况下在本地使用git。如果您不是唯一的开发人员,则服务器托管的存储库还使与他人的协作更加容易。

打包您的稳定代码

要考虑的一种非常简单的选择是,仅告诉您的用户从存储库中签出稳定分支。以这种方式进行分发并非闻所未闻。由于您不再需要手动收集所有文件,因此它仍比您当前的情况好一些。不过,您可能需要做一些文档记录。或者,如果您不想公开存储库,则可以使用源代码管理的内置功能以zip文件或类似文件(export在SVN和archivegit中)的形式检出整个提交。这些都可以上传到任何地方。

如果这还不够,您现在可以节省时间,setuptools可能是解决此问题的好方法。这将允许您生成一个包含稳定代码的转轮。您可以setup.py为要发布的每个代码包都有一个脚本。该setup.py脚本将标识要包括的软件包和模块。您必须手动管理此脚本,但是如果将其配置为包括整个程序包和目录,然后建立用于组织代码的良好项目约定,则不必经常更改它。这还具有为最终用户提供代码标准安装机制的好处。如果您希望广泛共享它,甚至可以在PyPI上发布它。

如果您愿意使用setuptools,则可能还需要考虑一个构建服务器,该服务器可以获取新的提交并可以运行脚本来重新打包并可能发布您的代码。


Col*_*lin 5

如果只需要模块,则可以运行代码和一个新的会话,然后遍历sys.modules软件包中的任何模块。

要使用PyCharm移动所有依赖项,您可以创建一个宏,将突出显示的对象移动到预定义的文件,将该宏附加到键盘快捷键,然后快速递归移动所有项目内导入。例如,我创建了一个名为的宏export_func,该宏将函数移至to_export.py并为F10添加了快捷方式:

宏动作

给定一个我想在文件中移动的功能

from utils import factorize


def my_func():
    print(factorize(100))
Run Code Online (Sandbox Code Playgroud)

utils.py看起来像

import numpy as np
from collections import Counter
import sys
if sys.version_info.major >= 3:
    from functools import lru_cache
else:
    from functools32 import lru_cache


PREPROC_CAP = int(1e6)


@lru_cache(10)
def get_primes(n):
    n = int(n)
    sieve = np.ones(n // 3 + (n % 6 == 2), dtype=np.bool)
    for i in range(1, int(n ** 0.5) // 3 + 1):
        if sieve[i]:
            k = 3 * i + 1 | 1
            sieve[k * k // 3::2 * k] = False
            sieve[k * (k - 2 * (i & 1) + 4) // 3::2 * k] = False
    return list(map(int, np.r_[2, 3, ((3 * np.nonzero(sieve)[0][1:] + 1) | 1)]))


@lru_cache(10)
def _get_primes_set(n):
    return set(get_primes(n))


@lru_cache(int(1e6))
def factorize(value):
    if value == 1:
        return Counter()
    if value < PREPROC_CAP and value in _get_primes_set(PREPROC_CAP):
        return Counter([value])
    for p in get_primes(PREPROC_CAP):
        if p ** 2 > value:
            break
        if value % p == 0:
            factors = factorize(value // p).copy()
            factors[p] += 1
            return factors
    for p in range(PREPROC_CAP + 1, int(value ** .5) + 1, 2):
        if value % p == 0:
            factors = factorize(value // p).copy()
            factors[p] += 1
            return factors
    return Counter([value])
Run Code Online (Sandbox Code Playgroud)

我可以突出显示my_func并按F10来创建to_export.py

from utils import factorize


def my_func():
    print(factorize(100))
Run Code Online (Sandbox Code Playgroud)

突出factorizeto_export.py击中F10得到

from collections import Counter
from functools import lru_cache

from utils import PREPROC_CAP, _get_primes_set, get_primes


def my_func():
    print(factorize(100))


@lru_cache(int(1e6))
def factorize(value):
    if value == 1:
        return Counter()
    if value < PREPROC_CAP and value in _get_primes_set(PREPROC_CAP):
        return Counter([value])
    for p in get_primes(PREPROC_CAP):
        if p ** 2 > value:
            break
        if value % p == 0:
            factors = factorize(value // p).copy()
            factors[p] += 1
            return factors
    for p in range(PREPROC_CAP + 1, int(value ** .5) + 1, 2):
        if value % p == 0:
            factors = factorize(value // p).copy()
            factors[p] += 1
            return factors
    return Counter([value])
Run Code Online (Sandbox Code Playgroud)

然后突出显示每个PREPROC_CAP_get_primes_setget_primes然后按F10得到

from collections import Counter
from functools import lru_cache

import numpy as np


def my_func():
    print(factorize(100))


@lru_cache(int(1e6))
def factorize(value):
    if value == 1:
        return Counter()
    if value < PREPROC_CAP and value in _get_primes_set(PREPROC_CAP):
        return Counter([value])
    for p in get_primes(PREPROC_CAP):
        if p ** 2 > value:
            break
        if value % p == 0:
            factors = factorize(value // p).copy()
            factors[p] += 1
            return factors
    for p in range(PREPROC_CAP + 1, int(value ** .5) + 1, 2):
        if value % p == 0:
            factors = factorize(value // p).copy()
            factors[p] += 1
            return factors
    return Counter([value])


PREPROC_CAP = int(1e6)


@lru_cache(10)
def _get_primes_set(n):
    return set(get_primes(n))


@lru_cache(10)
def get_primes(n):
    n = int(n)
    sieve = np.ones(n // 3 + (n % 6 == 2), dtype=np.bool)
    for i in range(1, int(n ** 0.5) // 3 + 1):
        if sieve[i]:
            k = 3 * i + 1 | 1
            sieve[k * k // 3::2 * k] = False
            sieve[k * (k - 2 * (i & 1) + 4) // 3::2 * k] = False
    return list(map(int, np.r_[2, 3, ((3 * np.nonzero(sieve)[0][1:] + 1) | 1)]))
Run Code Online (Sandbox Code Playgroud)

即使您要复制很多代码,它的运行速度也非常快。