繁体   English   中英

通过使用类避免python中的名称空间污染

[英]Avoiding namespace pollution in python by using Classes

有点背景

我正在编写供自己使用的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实例本身是不可调用的(???)。 虽然在类名称空间中定义的方法可以在内部和staticmethods均可调用,但仅通过类访问时才可调用classmethodsstaticmethods方法(此时它们引用基础函数,而不是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__函数中,然后将所有小变量_Loggingself ,但这也_Logging 我可以为此创建另一个文件,但是到目前为止,我已经将所有内容保存在同一文件中。 似乎很可口的唯一其他选择是使这两个函数成为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'
>>>

就目前而言,我认为最好的选择是使用无私方法。

很抱歉两年后回答,但这可以帮助某人。

您可以将您的方法设为静态,然后创建另一个静态方法(例如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()

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM