【发布时间】:2016-04-19 03:56:34
【问题描述】:
一点背景
我正在编写一个 Python 模块供我自己使用,并且我正在使用 Python 的 logging 模块。有处理程序和格式化程序,甚至我创建的一对函数(在大多数情况下)不会在其他任何地方使用。但是,我仍然希望能够在其他地方访问和修改这些变量(例如,其他紧密耦合的模块或脚本)
一个简单的命名空间
我目前正在做的是使用类定义将我的所有变量组合在一起,如下所示:
class _Logging:
'''A little namespace for our logging facilities. Don't try to instantiate
it: all it does is group together some logging objects and keep them out of
the global namespace'''
global logger
def __init__(self):
raise TypeError("that's not how this works...")
def gz_log_rotator(source, dest):
'''accept a source filename and a destination filename. copy source to
dest and add gzip compression. for use with
logging.handlers.RotatingFileHandler.rotator.'''
with gzip.open(dest, 'wb', 1) as ofile, open(source, 'rb') as ifile:
ofile.write(ifile.read())
os.remove(source)
def gz_log_namer(name):
'''accept a filename, and return it with ".gz" appended. for use with
logging.handlers.RotatingFileHandler.namer.'''
return name + ".gz"
fmtr = logging.Formatter(
'[%(asctime)s:%(name)s:%(thread)05d:%(levelname)-8s] %(message)s')
gz_rotfile_loghandler = logging.handlers.RotatingFileHandler(
'%s.log' % __name__, mode='a', maxBytes=(1024**2 * 20), backupCount=3)
gz_rotfile_loghandler.setLevel(5)
gz_rotfile_loghandler.setFormatter(fmtr)
gz_rotfile_loghandler.rotator = gz_log_rotator
gz_rotfile_loghandler.namer = gz_log_namer
simplefile_loghandler = logging.FileHandler(
'%s.simple.log' % __name__, mode='w')
simplefile_loghandler.setLevel(15)
simplefile_loghandler.setFormatter(fmtr)
stream_loghandler = logging.StreamHandler()
stream_loghandler.setLevel(25)
stream_loghandler.setFormatter(fmtr)
logger = logging.getLogger(__name__)
logger.setLevel(5)
logger.addHandler(gz_rotfile_loghandler)
logger.addHandler(simplefile_loghandler)
logger.addHandler(stream_loghandler)
但是,pylint 抱怨(我同意)类中定义的方法应该是静态方法,或者遵循第一个参数的命名约定(例如 gz_log_rotator(self, dest)),这不是函数的使用方式,并且会更加混乱。
趣事
在此过程中,我还发现 classmethod 和 staticmethod 的实例本身不可调用 (???)。虽然在类命名空间中定义的方法在内部和外部都可以调用,但 classmethods 和 staticmethods 仅在通过它们的类访问时才可调用(此时它们引用底层函数,而不是 classmethod/staticmethod对象)
>>> class Thing:
... global one_, two_, three_
... def one(self):
... print('one')
... @classmethod
... def two(cls):
... print('two')
... @staticmethod
... def three():
... print('three')
... one_, two_, three_ = one, two, three
...
>>> Thing.one()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: one() missing 1 required positional argument: 'self'
>>> Thing.two()
two
>>> Thing.three()
three
>>> # all as expected
>>> one_()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: one() missing 1 required positional argument: 'self'
>>> # so far so good
>>> two_()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'classmethod' object is not callable
>>> # what?
>>> three_()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'staticmethod' object is not callable
>>> # ???
我的问题
有没有更好的方法来保存这些变量而不污染我的命名空间?
我的代码可以正常工作,但它让我觉得有点不干净。我可以定义一个只被调用一次然后立即调用它的函数,但是我要么丢失对我不返回的所有内容的引用,要么我又回到了污染全局命名空间。我可以把所有东西都做成_hidden,但我觉得它们应该按逻辑分组。我可以让_Logging 成为一个真正的课程,将我所有的东西放在__init__ 函数中,并将我所有的小变量添加到self 上,但这也让人感觉不优雅。我可以为此创建另一个文件,但到目前为止,我已经将所有内容都保存在同一个文件中。唯一似乎可以接受的其他选项是使这两个函数staticmethods 并仅通过我们的类(即_Logging.gz_log_namer)引用它们,但这似乎也是不可能的。
>>> class Thing:
... @staticmethod
... def say_hello():
... print('hello!')
... Thing.say_hello()
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in Thing
AttributeError: type object 'Thing' has no attribute 'say_hello'
>>>
就目前而言,我认为最好的选择是使用无私的方法。
【问题讨论】:
-
为什么不用模块代替类,用函数代替方法呢?这将避免 self/cls 参数的所有问题以及不应实例化的类。
-
我试图尽可能长时间地将我的所有核心代码保存在一个文件中。否则,一个模块正是我正在寻找的。span>
标签: python namespaces