我有一个用于数据分析的课程。通常我需要创建该类的几个实例,然后在每个实例上运行一系列方法。我需要运行的方法因实例而异。
今天我的代码(使用该类)通常看起来像这样:
object_alpha = MyClass()
object_alpha.method1()
object_alpha.method2()
object_alpha.method3(arg1)
object_alpha.method4()
object_bravo = MyClass()
object_bravo.method1()
object_bravo.method3(arg1)
object_bravo.method4()
object_bravo.method5()
我知道上面的例子不是方法链接的例子。方法链接目前不可能,因为方法不返回类的对象。
这种格式有点重复,单调乏味,特别是对于长描述性变量名称。我的主要抱怨是我觉得它不太可读。
我想改变我的类以从每个方法调用返回一个新对象,这样我就可以进行方法链接。但是这些方法的副作用并没有改变类 - 它们通过API对数据库进行了更改,因此返回一个新对象会感觉很奇怪。
我的想法是创建一个将方法名称列表作为字符串的运行器类。所以我可以做这样的事情。
object_alpha = MyClass().runner([
'method1',
'method2',
'method3(arg1)',
'method4'
])
object_bravo = MyClass().runner([
'method1',
'method3(arg1)',
'method4',
'method5'
])
这是个坏主意吗?有更好的方法吗?
答案 0 :(得分:1)
你的想法很好,但它可以使用一个主要的改进:使用一个元组列表或类似的东西列表,而不是使用你必须评估的字符串列表,包含方法本身及其参数:
object_bravo = MyClass()
bravo_runner = [
(object_bravo.method1, (arg1, arg2), {k1: v1, k2, v2}),
(object_bravo.method2, (arg3), {}),
(object_bravo.method3, (), {k3: v3}),
(MyClass.method4, (object_bravo, arg4), {k4: v4})
]
运行它的方法比解析字符串容易得多:
for spec in bravo_runner:
spec[0](*spec[1], **spec[2])
如果你使用namedtuple
作为跑步者元素,它看起来会更好:
from collections import namedtuple
RunnerSpec = namedtuple('RunnerSpec', ['method', 'args', 'kwargs'])
object_bravo = MyClass()
bravo_runner = [
RunnerSpec(object_bravo.method1, (arg1, arg2), {k1: v1, k2, v2}),
RunnerSpec(object_bravo.method2, (arg3), {}),
RunnerSpec(object_bravo.method3, (), {k3: v3}),
RunnerSpec(MyClass.method4, (object_bravo, arg4), {k4: v4})
]
和run
方法:
for spec in bravo_runner:
spec.method(*spec.args, **spec.kwargs)
<强>结论强>
此时,您实际上可以通过在类中为每个使用self
而不是描述性对象名称的序列/方案编写方法来保存一些类型。这样可以最终节省你的时间,因为你会有预先构建的,命名的调用序列。无需将它们存储为列表。 bravo_object.run_scenario3()
优于bravo_object.runner([big_ass_hard_to_read_list_that_is_basically_a_method_anyway]).run()
。
答案 1 :(得分:1)
基本上,您在这里尝试发明特定于域的语言。因为它的唯一目的是执行类似Python的东西,只需要不必命名上下文对象,我就不认为它值得麻烦。从一个方法返回一个对象以允许方法链接的模式,即使该对象不是操作的逻辑结果,实际上也不是闻所未闻的,所以我会去那。