英文:
Where to put a small utility function that I would like to use across multiple packages/projects that I develop?
问题
我有一个对我所工作的多个不同包都有用的功能。这个函数只有几行代码。但我想要能够在多个我正在工作和部署的包/项目中使用这段代码,我希望这段代码能够进行版本控制等。例如,并没有一个所有其他包都已经要求的包,否则我可以将这段代码放在那个包中并以那种方式导入它。
在编码过程中,我遇到了这个问题几次。以具体示例,一些具有这种特性的函数f
可能包括:
- 一个包装器或上下文管理器,用于计时一段带有一些日志语句的代码块
- 一个将整数范围划分为尽可能小的均匀间隔步数的函数,同时不超过最大步数
- 一个将中心值和跨度转换为下限和上限的函数
基本上,我看到的选项有:
- 将代码
f
放入现有的包R
中,然后使希望使用f
的包A
将R
作为要求。这种方法的缺点是,A
可能只需要f
而不需要R
中的其他任何内容,这样大部分的要求就会浪费掉。 - 将代码
f
复制到每个需要它的包A
中。然后出现一个问题,f
应该放在A
的哪个位置?因为f
的功能实际上超出了包A
的范围。这是一个小问题,更大的问题是,如果对f
进行了改进,将很难在多个包中保持一致性,这些包中都有f
。 - 我可以创建一个专门用于像
f
这样的函数的整个包F
,然后将其导入到需要f
的每个包中。从要求管理和责任分离管理的角度来看,这似乎在技术上是最佳方法。但正如我所说,目前这将是一个专门用于只有几行代码的一个函数的整个包。 - 如果有一个标准库函数具有我想要的功能,那么我肯定应该使用它。如果没有,我可能能够找到一个具有我想要功能的第三方包,但这会带来其他潜在问题的一系列,我宁愿避免。
有什么建议的方法吗?还有其他方法我没有提到的吗?
英文:
Right now I have one function that would be useful in a number of distinct packages that I work on. The function is only a handful of lines. But I would like to be able to use this code in a number of packages/projects that I work on and are deployed, I would like this code to be version controlled etc. There isn't, for example, one package that all the other packages already have as a requirement, otherwise I could put this code inside of that one and import it that way.
During my time coding I've come across this issue a couple times. For concreteness some functions f
that have this characteristic might be:
- A wrapper or context manager which times a block of code with some log statements
- A function which divides a range of integers into as small number of evenly spaced strides while not exceeding a maximum number of steps
- A function which converts a center value and a span into a lower and upper limit
Basically the options I see are:
- Put the code
f
in one of the existing packagesR
and then make any packageA
that wants to usef
haveR
as a requirement. The downside here is thatA
may require nothing fromR
other thanf
in which case most of the requirement is wasteful. - Copy the code
f
into every packageA
that requires it. One question that comes up is then where shouldf
live withinA
? Because the functionality off
is really outside the scope of packageA
does. This is sort of a minor problem, the bigger problem is that if an improvement is made tof
in one package it would be challenging to maintain uniformity across multiple packages that havef
in them. - I could make an entire package
F
dedicated to functions likef
and import it into each package that needsf
. This seems like technically the best approach from a requirements management and separation of responsibility management perspective. But like I said, right now this would be an entire package dedicated to literally one function with a few lines of code. - If there is a stdlib function that has the functionality I want I should definitely use that. If there is not, I may be able to find a 3rd party package that has the functionality I want, but this brings about a zoo of other potential problems that I'd prefer to avoid.
What would be the suggested way to do this? Are there other approaches I haven't mentioned?
答案1
得分: 3
整个打包系统的设计是为了解决这个问题 - 在不同应用程序之间共享代码。因此,理想情况下,你应该将这个代码创建成一个包,并将其添加为所有使用此代码的其他包的依赖项。这个选项有一些好处:
- 包管理更加清晰
- 未来的存储库也可以包含此代码
- 对此代码的任何更改仍然可以通过适当的版本控制和版本锁定来处理,从而不会破坏其他地方的代码
- 未来的类似函数
f2
、f3
等可以潜在地添加到这个包中,从而允许你在包之间共享它们
但这也伴随着一些(潜在的)缺点:
- 现在你需要维护一个额外的包,包括其部署管道和版本控制 - 但如果已经有一个管道,这应该不会太麻烦
- 糟糕的版本控制管理可能会在引入破坏性更改时迅速导致系统崩溃 - 这通常更难追踪
话虽如此,复制代码到使用 f
的每个包仍然是一个选项。考虑以下几点:
- 通常,这样的代码随着时间的推移也会被调整以适应父包的要求,在这种情况下,将其在包之间共享不再有意义 - 而且试图通用化它往往导致糟糕的抽象。如果遵循 DRY 是你的关注点,请查看 Dan Abramov 关于“WET 代码库”的讲座。
- 关于保持统一性 - 你可能并不总是需要这样做,这取决于用例。包
A
可能使用更新的代码,而包B
可能使用旧的代码。无论采用哪种方法,你仍然需要更新每个包以保持统一性 - 例如,如果选择了专用包,你仍然需要更新到处使用的版本。 - 关于此代码将在每个包的代码库中的位置 - 如果
f
做一些非常具体的事情,它可以放在一个适当命名的文件中。如果没有其他办法,总是可以使用臭名昭著的util.py
¯\(ツ)/¯
建议
- 首先将代码复制到所有包中。根据每个包的需求分别进行更新。
- 随着时间的推移,如果你观察到对
f
的任何更新每次都传播到所有其他包中,那么将f
放入一个独立的包中,并将其他包中的代码替换为从这个新包中导入。 - 最后,不要为小事烦恼。软件中的大多数事情都是可逆的。选择一种方法,如果不起作用,可以更改它。只要记住不要拖延决策 - 推迟太久,你将留下大量的技术债务。
附言: 有人可能建议使用 git 子模块来共享这个代码 - 不要这样做,管理版本不够清晰,很快会失控 - 你最好创建一个新的包
英文:
The entire packaging system is designed to solve exactly this problem - sharing code between different applications. So yes, the ideally you'd want to create a package out of this and add it as a dependency to all the other packages that use this code. There are a few upsides to this option:
- Package management is clean
- Future repositories can also include this code
- Any changes to this code can still be handled with proper versioning and version pinning - thus not breaking code in other places
- Future such functions
f2
,f3
, etc. can potentially be added to this package, allowing you to share them across packages too
But this also comes with some (potential) downsides:
- You now have to maintain an additional package, complete with its deployment pipeline and versioning - this however should not be too much of a hassle if there is already a pipeline in place
- Poorly managed versioning can cause systems to collapse rather quickly, whenever breaking changes are introduced - this typically is harder to trace
Having said that, the option of copying code to each of the packages that use f
is still an option. Consider these points:
- Often, such code is also tweaked over time to adapt it to the requirements of the parent package, in such cases sharing it between packages no longer makes sense - and attempts to generalize it more often than not lead to bad abstractions. If adhering to DRY is your concern, do checkout this talk from Dan Abramov on the 'WET codebase'
- Regarding maintaining uniformity - you may not have to do so all the time, depending on the usecase. Package
A
could be using updated code, while packageB
could be using the older one. Regardless, whatever approach you use, you'd still need to update every package to maintain uniformity - for example if you go with a dedicated package, you'd still need to update the version used everywhere. - Regarding where this code will reside in each package's codebase - If
f
does something very specific, it can reside in an appropriately named file of its own. If nothing else, there is always the notoriously overusedutil.py
¯\(ツ)/¯
Recommendation
- Begin with copying over the code to all packages. Update them individually as required in every package.
- Over time if you observe that any updates to
f
is being propagated to all other packages every time, then putf
in a package of its own and replace the code in the other packages with an import from this new package. - Finally, don't fret the small things. Most things in software are reversible. Pick one approach and change it to the other if it does not workout. Just remember to not drag the decision - delay too much and you'd be left with a huge mountain of tech debt over time.
PS: Someone may recommend using a git submodule for sharing this code - DO NOT do it, managing versions isn't clean and will soon get out of hand - you'd rather just create a new package instead
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论