我正在编写一个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
只有在通过类访问时才可调用(此时它们引用底层函数,而不是{{1} } / 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
就目前而言,我看到的最佳选择是使用无私方法。
答案 0 :(得分:0)
很抱歉2年后回答,但这可以帮到某人。
你可以使你的方法变为静态,并创建另一个静态方法(例如init
),在初始化类之后立即调用它。然后使用setattr
保留对变量的引用。
要设置多个类变量,可以使用
[setattr(Class, name, value) for name,value in locals().items()]
在方法内部。
完整代码:
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'''
def __init__(self):
raise TypeError("that's not how this works...")
@staticmethod
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)
@staticmethod
def gz_log_namer(name):
'''accept a filename, and return it with ".gz" appended. for use with
logging.handlers.RotatingFileHandler.namer.'''
return name + ".gz"
@staticmethod
def init():
global logger
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 = _Logging.gz_log_rotator
gz_rotfile_loghandler.namer = _Logging.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)
[setattr(_Logging, name, value) for name,value in locals().items()]
_Logging.init()