使用类

时间:2016-04-19 03:56:34

标签: python namespaces

一点背景

我正在编写一个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)),这不是函数的使用方式,而且会更令人困惑。

有趣的事实

在此过程中,我还发现classmethodstaticmethod的实例本身不可调用(???)。虽然类命名空间中定义的方法可以在内部和外部调用,但classmethodsstaticmethods只有在通过类访问时才可调用(此时它们引用底层函数,而不是{{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

就目前而言,我看到的最佳选择是使用无私方法。

1 个答案:

答案 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()