【问题标题】:Avoiding namespace pollution in python by using Classes使用类避免python中的命名空间污染
【发布时间】: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)),这不是函数的使用方式,并且会更加混乱。

趣事

在此过程中,我还发现 classmethodstaticmethod 的实例本身不可调用 (???)。虽然在类命名空间中定义的方法在内部和外部都可以调用,但 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__ 函数中,并将我所有的小变量添加到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


【解决方案1】:

您可以创建一个继承自staticmethod 类的新类,并将__call__ 方法添加到该类中。
例如:

class callablestatic(staticmethod):
    def __init__(self, func):
        super().__init__(func)
        self.func = func
    def __call__(self, *args, **kwargs):
        # the __call__ method allows you to call the class instance
        return self.func(*args, **kwargs)

然后在你的课堂上使用它:

class Thing:
    @callablestatic
    def hello(name):
        print(f"hello {name}")
    hello("John") # works

但最好创建新文件并将其作为模块导入

【讨论】:

    【解决方案2】:

    很抱歉 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()
    

    【讨论】:

      猜你喜欢
      • 2020-01-03
      • 2011-05-14
      • 2015-03-05
      • 2023-04-03
      • 1970-01-01
      • 1970-01-01
      • 2010-12-22
      • 2011-06-04
      • 1970-01-01
      相关资源
      最近更新 更多