dwo*_*dwo 3 python oop refactoring
我没有很多面向对象Python的经验,想要重构一个简单的命令行工具.我当前的脚本只是导入了所需的库,有一些函数定义,使用全局变量(我知道这是不好的做法)并在一个.py文件中使用argparse,例如:
import argparse
dict = {}
#Some code to populate the dict, used both in checking the argument and later in the script
def check_value(value):
if not dict.has_key(value):
raise argparse.ArgumentTypeError("%s is invalid." % value)
return value
parser = argparse.ArgumentParser(…)
parser.add_argument('…', type=check_value, help='…')
args = parser.parse_args()
# Some other code that uses the dict
Run Code Online (Sandbox Code Playgroud)
同样,我处理一些参数解析的方式使用类似于"abc"的函数来修改我稍后在脚本中需要的字典.我怎么让这个不那么难看?或者使用简单命令行脚本可接受的全局变量?
让我们创建一个模仿我们的dict的单个类,并将其传递给这些方法,而不是对全局变量进行操作,你可以告诉我这是否更符合你的要求.事实是,你已经在使用一些面向对象的编程并且不知道它(dict类)
Python面向对象编程围绕着类,它可以被实例化一次或多次,并且这些使用在它们上定义的函数.然后我们调用方法classname.class_method(input_variables)并获取返回值,就像我们调用非绑定函数一样.事实上,全局函数和显式绑定到类实例的函数之间存在明确的差异.这是"绑定"和"未绑定"方法之间的区别,也是我们获得魔术名称的地方self.
class ExampleDict(object):
#Called when a new class instance is created
def __init__(self, test):
self.dict = {}
self.dict["test"] = test
#Called when len() is called with a class instance as an argument
def __len__(self):
return len(self.dict)
#Called when the internal dict is accessed by instance_name[key]
def __getitem__(self, key):
return self.dict[key]
#Called when a member of the internal dict is set by
#instance_name[key] = value
def __setitem__(self, key, value):
self.dict[key] = value
#Called when a member of the internal dict is removed by del instance[key]
def __delitem__(self, key):
del self.dict[key]
#Return an iterable list of (key, value) pairs
def get_list(self):
return self.dict.items()
#Replace your global function with a class method
def check_value(self, key):
if not self.dict.has_key(value):
raise argparse.ArgumentTypeError("%s is invalid." % value)
return value
Run Code Online (Sandbox Code Playgroud)
几点说明:
d = ExampleDict()现在,我们想要删除全局变量的使用.在许多情况下,如上所述,我们可以将您的方法转换为类方法.如果这不合适,您可以接受一个对象作为全局函数的参数.在这种情况下,我们希望您的方法接受它作为参数操作的字典,而不是直接对全局变量进行操作
def check_value_global(inp_dict, value):
if not inp_dict.has_key(value):
raise argparse.ArgumentTypeError("%s is invalid." % value)
return value
Run Code Online (Sandbox Code Playgroud)
现在,让我们定义当我们运行脚本并声明一些类实例时会发生什么,然后将它们传递给方法或执行它们的类方法:
if __name__ == "__main__":
#Declare an instance of the class
ex = ExampleDict("testing")
print(ex["test"])
ex["test2"] = "testing2"
check_value_global(ex, "test")
print("At next section")
ex.check_value("test2")
print("At final section")
Run Code Online (Sandbox Code Playgroud)
有关Python中面向对象编程功能的更深入讨论,请参见此处
好的,让我们来看看特别是argparse.这将解析提供给脚本的命令行参数(这里的替代方法只是从sys.argv读取).
从理论上讲,我们可以将它包含在任何地方,但我们确实应该直接包含它if __name__=="__main__":,或者在此语句之后调用的方法中包含它.为什么?
只有在直接从命令行调用python脚本而不是作为模块导入时,才会运行此语句之后的任何内容.假设你想将你的脚本导入另一个python脚本并在那里使用它.在这种情况下,您不需要命令行参数,因此您不希望尝试解析它们.
尽管如此,我们现在知道我们在主段(后if __name__=="__main__":)中初始化了一个dict对象和一个argparse对象.我们如何将它们传递给程序中上面定义的函数和类?
我们有很多选择,我最常用的是:
这是一个例子:
class SingletonExample(object):
def __init__(self, dict_obj, arg_obj):
self.dict = dict_obj
self.args = arg_obj
def some_script_function(self):
pass
#Use your self.dict and self.args arguments
Run Code Online (Sandbox Code Playgroud)
事实上,你真的在谈论设计模式,你解决这个问题的方式将由你选择的设计决定.例如,这里的第三种解决方案通常被称为单例模式.确定最适合手头任务的模式,对其进行一些研究,这将告诉您如何构建对象和方法.