tance(self.get(name), _Option):
return self[name].value()
raise AttributeError("Unrecognized option %r" % name)options = _Options.instance()
我们看看,_Options实际上是一个字典,其定义如上,使用了一个单例模式,注意到hasattr的使用,以及__getattr__魔法方法的使用。我们看看值类型_Option
定义:
class _Option(object):
def __init__(self, name, default=None, type=str, help=None, metavar=None,
multiple=False, file_name=None):
if default is None and multiple:
default = []
self.name = name
self.type = type
self.help = help
self.metavar = metavar
self.multiple = multiple
self.file_name = file_name
self.default = default
# 默认值为None
self._value = None
_Option的parse方法
在parse_command_line中有一句option.parse(value),其根据将参数传入的值,设置option为该值。
def parse(self, value):
# 注意这种字典方式
# 其作用是根据参数的类型(比如:是一个简单的选项,或者其值应该为字符串)
# 选择相应的解析函数。
_parse = {
#
datetime.datetime: self._parse_datetime,
datetime.timedelta: self._parse_timedelta,
bool: self._parse_bool,
str: self._parse_string,
}.get(self.type, self.type)
# 值有多个
if self.multiple:
if self._value is None:
self._value = []
# 获取每个
for part in value.split(","):
if self.type in (int, long):
# allow ranges of the form X:Y (inclusive at both ends)
lo, _, hi = part.partition(":")
# 对于int, long type,_parse返回的是int()和long()方法,
# 很巧妙
lo = _parse(lo)
hi = _parse(hi) if hi else lo
# 设置添加多个系列值
self._value.extend(range(lo, hi+1))
else:
self._value.append(_parse(part))
else:
self._value = _parse(value)
return self.value()
_parse = { xxxx}.get(self.type, self.type)
有点继承的味道,根据传入值的类型,来选择相应的解析方式。实现的比较优雅,值得学习。
define函数
我们知道tornado实际上在项目中,在options
添加了几个默认的_Option
对象。
define("help", type=bool, help="show this help information")
define("logging", default="info",
help=("Set the Python log level. If 'none', tornado won't touch the "
"logging configuration."),
metavar="info|warning|error|none")
define("log_to_stderr", type=bool, default=None,
help=("Send log output to stderr (colorized if possible). "
"By default use stderr if --log_file_prefix is not set and "
"no other logging is configured."))
define("log_file_prefix", type=str, default=None, metavar="PATH",
help=("Path prefix for log files. "
"Note that if you are running multiple tornado processes, "
"log_file_prefix must be different for each of them (e.g. "
"include the port number)"))
define("log_file_max_size", type=int, default=100 * 1000 * 1000,
help="max size of log files before rollover")
define("log_file_num_backups", type=int, default=10,
help="number of log files to keep")
具体实现:
if name in options:
# 重复定义不允许
raise Error("Option %r already defined in %s", name,
options[name].file_name)
frame = sys._getframe(0)
# 用来获取当前文件名
options_file = frame.f_code.co_filename
file_name = frame.f_back.f_code.co_filename
# 如果是在options.py使用了define这个函数,
# 那么file_name就默认为空
if file_name == options_file: file_name = ""
options[name] = _Option(name, file_name=file_name, default=default,
type=type, help=help, metavar=metavar,
multiple=multiple)
sys._getframe(0)表示调用栈帧顶部,也就是define这个函数。下面的frame.f_code.co_filename表示定义define这个函数脚本名称(包含路径), 后面的fr