python – 为频繁访问的对象创建一个钩子
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了python – 为频繁访问的对象创建一个钩子,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含5910字,纯文字阅读大概需要9分钟。
内容图文
我有一个应用程序,它严重依赖于一个Context实例,该实例充当执行给定计算的上下文的访问点.
如果我想提供对Context实例的访问,我可以:
>依靠全球化
>将Context作为参数传递给需要它的所有函数
我宁愿不使用全局变量,并且将Context实例传递给所有函数是麻烦且冗长的.
你如何“隐藏,但可以访问”计算上下文?
例如,假设Context仅根据不同的数据计算行星的状态(位置和速度).
class Context(object):
def state(self, planet, epoch):
"""base class --- suppose `state` is meant
to return a tuple of vectors."""
raise NotImplementedError("provide an implementation!")
class DE405Context(Context):
"""Concrete context using DE405 planetary ephemeris"""
def state(self, planet, epoch):
"""suppose that de405 reader exists and can provide
the required (position, velocity) tuple."""
return de405reader(planet, epoch)
def angular_momentum(planet, epoch, context):
"""suppose we care about the angular momentum of the planet,
and that `cross` exists"""
r, v = context.state(planet, epoch)
return cross(r, v)
# a second alternative, a "Calculator" class that contains the context
class Calculator(object):
def __init__(self, context):
self._ctx = context
def angular_momentum(self, planet, epoch):
r, v = self._ctx.state(planet, epoch)
return cross(r, v)
# use as follows:
my_context = DE405Context()
now = now() # assume this function returns an epoch
# first case:
print angular_momentum("Saturn", now, my_context)
# second case:
calculator = Calculator(my_context)
print calculator.angular_momentum("Saturn", now)
当然,我可以将所有操作直接添加到“上下文”中,但感觉不对.
在现实生活中,上下文不仅计算行星的位置!它计算了更多的东西,它作为大量数据的访问点.
所以,为了使我的问题更简洁:你如何处理需要被许多类访问的对象?
我目前正在探索:python的上下文管理器,但没有太多运气.我还考虑过直接向所有函数动态添加属性“context”(函数是对象,因此它们可以有任意对象的访问点),即:
def angular_momentum(self, planet, epoch):
r, v = angular_momentum.ctx.state(planet, epoch)
return cross(r, v)
# somewhere before calling anything...
import angular_momentum
angular_momentum.ctx = my_context
编辑
一些很棒的东西,就是用with语句创建一个“计算上下文”,例如:
with my_context:
h = angular_momentum("Earth", now)
当然,如果我简单地写一下,我已经可以这样做了:
with my_context as ctx:
h = angular_momentum("Earth", now, ctx) # first implementation above
也许这与Strategy pattern的变化?
解决方法:
你通常不想在Python中“隐藏”任何东西.你可能想告诉人类读者他们应该把它当作“私人”,但这实际上只是意味着“你应该能够理解我的API,即使你忽略了这个对象”,而不是“你无法访问它”.
在Python中执行此操作的惯用方法是在其前面添加下划线 – 如果您的模块可能与foo import *一起使用,则添加一个列出所有公共导出的显式__all__全局.同样,这些都不会实际阻止任何人看到你的变量,甚至在导入foo后从外部访问它.
有关详细信息,请参阅全局变量名称上的PEP 8.
一些样式指南建议使用特殊前缀,全部大写字母或全局变量的其他特殊区别标记,但PEP 8明确指出约定是相同的,除了__all__和/或前导下划线.
同时,您想要的行为显然是全局变量 – 每个人都隐含地共享和引用的单个对象.试图将它伪装成除了它之外的任何东西对你没有好处,除了可能通过lint检查或你不应该通过的代码审查.全局变量的所有问题都来自于每个人隐含地共享和引用的单个对象,而不是直接在globals()字典或类似的东西中,所以任何体面的假全局都和真正的全局一样糟糕.如果这确实是您想要的行为,请将其设为全局变量.
把它放在一起:
# do not include _context here
__all__ = ['Context', 'DE405Context', 'Calculator', …
_context = Context()
当然,您可能想要将其称为_global_context甚至_private_global_context,而不仅仅是_context.
但请记住,全局变量仍然是模块的成员,而不是整个Universe的成员,因此当客户端代码执行导入foo时,即使公共上下文仍将作为foo.context进行范围设定.这可能正是你想要的.如果你想要一种方法让客户端脚本导入你的模块然后控制它的行为,也许foo.context = foo.Context(…)是正确的方法.当然,这在多线程(或gevent / coroutine / etc.)代码中不起作用,并且在其他各种情况下都不合适,但如果这不是问题,在某些情况下,这很好.
因为你在评论中提出了多线程:在多线程的简单风格中,你有长时间运行的工作,全局风格实际上工作得非常好,有一个微不足道的变化 – 用包含Context的全局threading.local实例替换全局Context.即使在线程池处理小作业的风格中,也不会复杂得多.您将上下文附加到每个作业,然后当工作人员从队列中取出作业时,它会将线程本地上下文设置为该作业的上下文.
但是,我不确定多线程是否适合您的应用程序.当您的任务偶尔必须阻止IO并且您希望能够在不停止其他任务的情况下执行此操作时,多线程在Python中很棒 – 但是,由于GIL,它对于并行化CPU工作几乎没用,而且听起来就像是你的正在寻找.多处理(无论是通过多处理模块还是其他方式)可能更多地是您所追求的.通过单独的流程,保持单独的上下文更加简单. (或者,您可以编写基于线程的代码并将其切换到多处理,将threading.local变量保留为原样,只更改生成新任务的方式,一切都运行正常.)
在上下文管理器意义上提供“上下文”可能是有意义的,作为标准库decimal模块的外部版本,所以有人可以写:
with foo.Context(…):
# do stuff under custom context
# back to default context
但是,没有人能真正想到一个好的用例(特别是因为,至少在天真的实现中,它实际上并没有解决线程/等问题),所以它没有被添加到标准库中,并且你可能也不需要它.
如果你想这样做,那就太微不足道了.如果您正在使用私有全局,只需将其添加到Context类:
def __enter__(self):
global _context
self._stashedcontext = _context
_context = self
def __exit__(self, *args):
global context
_context = self._stashedcontext
显而易见的是如何将其调整为公共,线程本地等替代方案.
另一种方法是使一切成为Context对象的成员.然后,顶级模块函数只委托给具有合理默认值的全局上下文.这正是标准库random模块的工作原理 – 你可以创建一个random.Random()并在其上调用randrange,或者你可以调用random.randrange(),它在全局默认random.Random上调用相同的东西( )对象.
如果创建一个Context太重而无法在导入时执行,特别是如果它可能没有被使用(因为没有人可能会调用全局函数),您可以使用单例模式在第一次访问时创建它.但这很少是必要的.如果不是,那么代码就是微不足道的.例如,从第881行开始随机的source执行此操作:
_inst = Random()
seed = _inst.seed
random = _inst.random
uniform = _inst.uniform
…
这就是它的全部内容.
最后,正如您所建议的那样,您可以使所有内容成为拥有Context对象的不同Calculator对象的成员.这是传统的OOP解决方案;过度使用它往往会让Python感觉像Java,但在适当的时候使用它并不是一件坏事.
内容总结
以上是互联网集市为您收集整理的python – 为频繁访问的对象创建一个钩子全部内容,希望文章能够帮你解决python – 为频繁访问的对象创建一个钩子所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。