我有一个很长的函数def func(browser):
# make sure we are logged in otherwise log in
# make request to /search and check that the page has loaded
# fill form in /search and submit it
# read table of response and return the result as list of objects
,它接受浏览器处理并执行一堆请求,并按特定顺序读取一堆响应:
func
由于DOM的复杂性,每个操作都需要大量代码,并且它们的增长速度非常快。
将此函数重构为较小的组件的最佳方法是什么,以便以下属性仍然存在:
$(function () {
$("#txtFrom").datepicker({
numberOfMonths: 2,
onSelect: function (selected) {
var dt = new Date(selected);
dt.setDate(dt.getDate() + 1);
$("#txtTo").datepicker("option", "minDate", dt);
}
});
$("#txtTo").datepicker({
numberOfMonths: 2,
onSelect: function (selected) {
var dt = new Date(selected);
dt.setDate(dt.getDate() - 1);
$("#txtFrom").datepicker("option", "maxDate", dt);
}
});
});
可以在浏览器上多次调用
答案 0 :(得分:4)
只需将三个辅助方法包装在一个类中,并跟踪允许在实例中运行的方法。
class Helper(object):
def __init__(self):
self.a = True
self.b = False
self.c = False
def funcA(self):
if not self.A:
raise Error("Cannot run funcA now")
# do stuff here
self.a = False
self.b = True
return whatever
def funcB(self):
if not self.B:
raise Error("Cannot run funcB now")
# do stuff here
self.b = False
self.c = True
return whatever
def funcC(self):
if not self.C:
raise Error("Cannot run funcC now")
# do stuff here
self.c = False
self.a = True
return whatever
def func(...):
h = Helper()
h.funcA()
h.funcB()
h.funcC()
# etc
调用方法的唯一方法是,如果其标志为true,并且每个方法都清除其自己的标志并在退出之前设置下一个方法的标志。只要你不碰h.a
等。直接,这确保每个方法只能以正确的顺序调用。
或者,您可以使用单个标志,该标志是对当前允许运行的函数的引用。
class Helper(object):
def __init__(self):
self.allowed = self.funcA
def funcA(self):
if self.allowed is not self.funcA:
raise Error("Cannot run funcA now")
# do stuff
self.allowed = self.funcB
return whatever
# etc
答案 1 :(得分:2)
这是我提出的解决方案。我使用了一个装饰器(与this blog post中的装饰密切相关),它只允许调用一次函数。
def call_only_once(func):
def new_func(*args, **kwargs):
if not new_func._called:
try:
return func(*args, **kwargs)
finally:
new_func._called = True
else:
raise Exception("Already called this once.")
new_func._called = False
return new_func
@call_only_once
def stateA():
print 'Calling stateA only this time'
@call_only_once
def stateB():
print 'Calling stateB only this time'
@call_only_once
def stateC():
print 'Calling stateC only this time'
def state():
stateA()
stateB()
stateC()
if __name__ == "__main__":
state()
你会看到,如果你重新调用任何函数,该函数将抛出一个Exception
,说明函数已被调用。
问题在于,如果您需要再次拨打state()
,那么您就会受到冲击。除非你将这些函数实现为私有函数,否则由于Python的作用域规则的性质,我认为你不能完全完全你想要的东西。
您还可以移除装饰器中的else
,并且您的函数将始终返回None
。
答案 2 :(得分:1)
这是我用过状态机的一段代码
class StateMachine(object):
def __init__(self):
self.handlers = {}
self.start_state = None
self.end_states = []
def add_state(self, name, handler, end_state=0):
name = name.upper()
self.handlers[name] = handler
if end_state:
self.end_states.append(name)
def set_start(self, name):
# startup state
self.start_state = name
def run(self, **kw):
"""
Run
:param kw:
:return:
"""
# the first .run call call the first handler with kw keywords
# each registered handler should returns the following handler and the needed kw
try:
handler = self.handlers[self.start_state]
except:
raise InitializationError("must call .set_start() before .run()")
while True:
(new_state, kw) = handler(**kw)
if isinstance(new_state, str):
if new_state in self.end_states:
print("reached ", new_state)
break
else:
handler = self.handlers[new_state]
elif hasattr(new_state, "__call__"):
handler = new_state
else:
return
使用
class MyParser(StateMachine):
def __init__(self):
super().__init__()
# define handlers
# we can define many handler as we want
self.handlers["begin_parse"] = self.begin_parse
# define the startup handler
self.set_start("begin_parse")
def end(self, **kw):
logging.info("End of parsing ")
# no callable handler => end
return None, None
def second(self, **kw):
logging.info("second ")
# do something
# if condition is reach the call `self.end` handler
if ...:
return self.end, {}
def begin_parse(self, **kw):
logging.info("start of parsing ")
# long process until the condition is reach then call the `self.second` handler with kw new keywords
while True:
kw = {}
if ...:
return self.second, kw
# elif other cond:
# return self.other_handler, kw
# elif other cond 2:
# return self.other_handler 2, kw
else:
return self.end, kw
# start the state machine
MyParser().run()
将打印
INFO:root:start of parsing
INFO:root:second
INFO:root:End of parsing
答案 3 :(得分:0)
您可以在func
功能中使用本地功能。好吧,它们仍然在一个单一的全局函数中声明,但是Python足够好,仍然可以让你访问它们进行测试。
这是一个函数声明和执行3(假定重)子函数的一个例子。它需要一个可选参数test
,当设置为TEST
时会阻止实际执行,而是提供对各个子函数和局部变量的外部访问:
def func(test=None):
glob = []
def partA():
glob.append('A')
def partB():
glob.append('B')
def partC():
glob.append('C')
if (test == 'TEST'):
global testA, testB, testC, testCR
testA, testB, testC, testCR = partA, partB, partC, glob
return None
partA()
partB()
partC()
return glob
当您致电func
时,3个部分将按顺序执行。但是,如果您先拨打func('TEST')
,则可以将glob
变量视为testCR
,将3个子功能设为testA
,testB
和{{1 }}。这样,您仍然可以使用定义良好的输入单独测试3个部件并控制其输出。
答案 4 :(得分:0)
我会坚持@ user3159253在对原始问题的评论中提出的建议:
如果唯一的目的是可读性,我会将功能分成三个“私人”>或“受保护的”(即_func1或__func1)和私人或受保护的财产>这使得状态在函数之间保持共享。
这对我来说很有意义,在面向对象编程中似乎比其他选项更常见。考虑这个例子作为替代:
你的班级(teste.py):
class Test:
def __init__(self):
self.__environment = {} # Protected information to be shared
self.public_stuff = 'public info' # Accessible to outside callers
def func(self):
print "Main function"
self.__func_a()
self.__func_b()
self.__func_c()
print self.__environment
def __func_a(self):
self.__environment['function a says'] = 'hi'
def __func_b(self):
self.__environment['function b says'] = 'hello'
def __func_c(self):
self.__environment['function c says'] = 'hey'
其他档案:
from teste import Test
t = Test()
t.func()
这将输出:
Main function says hey guys
{'function a says': 'hi', 'function b says': 'hello', 'function c says': 'hey'}
如果您尝试调用其中一个受保护的函数,则会发生错误:
Traceback (most recent call last):
File "C:/Users/Lucas/PycharmProjects/testes/other.py", line 6, in <module>
t.__func_a()
AttributeError: Test instance has no attribute '__func_a'
如果您尝试访问受保护的环境变量,则相同:
Traceback (most recent call last):
File "C:/Users/Lucas/PycharmProjects/testes/other.py", line 5, in <module>
print t.__environment
AttributeError: Test instance has no attribute '__environment'
在我看来,这是解决问题的最优雅,简单和可读的方式,让我知道它是否符合您的需求:)