标签归档:configparser

Python扩展-使用super()Python 3 vs Python 2

问题:Python扩展-使用super()Python 3 vs Python 2

本来我想问这个问题,但是后来我发现它已经被想到了……

在谷歌搜索中发现了扩展configparser的示例。以下适用于Python 3:

$ python3
Python 3.2.3rc2 (default, Mar 21 2012, 06:59:51) 
[GCC 4.6.3] on linux2
>>> from configparser import  SafeConfigParser
>>> class AmritaConfigParser(SafeConfigParser):
...     def __init_(self):
...         super().__init__()
... 
>>> cfg = AmritaConfigParser()

但不适用于Python 2:

>>> class AmritaConfigParser(SafeConfigParser):
...       def __init__(self):
...           super(SafeConfigParser).init()
... 
>>> cfg = AmritaConfigParser()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __init__
TypeError: must be type, not classob

然后,我读了一些关于Python New Class vs. Old Class样式的信息(例如,在这里。现在我想知道,我可以这样做:

class MyConfigParser(ConfigParser.ConfigParser):
      def Write(self, fp):
          """override the module's original write funcition"""
          ....
      def MyWrite(self, fp):
          """Define new function and inherit all others"""

但是,我不应该叫init吗?这在Python 2中是否等效:

 class AmritaConfigParser(ConfigParser.SafeConfigParser):
    #def __init__(self):
    #    super().__init__() # Python3 syntax, or rather, new style class syntax ...
    #
    # is this the equivalent of the above ? 
    def __init__(self):
        ConfigParser.SafeConfigParser.__init__(self)

Originally I wanted to ask this question, but then I found it was already thought of before…

Googling around I found this example of extending configparser. The following works with Python 3:

$ python3
Python 3.2.3rc2 (default, Mar 21 2012, 06:59:51) 
[GCC 4.6.3] on linux2
>>> from configparser import  SafeConfigParser
>>> class AmritaConfigParser(SafeConfigParser):
...     def __init_(self):
...         super().__init__()
... 
>>> cfg = AmritaConfigParser()

But not with Python 2:

>>> class AmritaConfigParser(SafeConfigParser):
...       def __init__(self):
...           super(SafeConfigParser).init()
... 
>>> cfg = AmritaConfigParser()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __init__
TypeError: must be type, not classob

Then I read a little bit on Python New Class vs. Old Class styles (e.g. here. And now I am wondering, I can do:

class MyConfigParser(ConfigParser.ConfigParser):
      def Write(self, fp):
          """override the module's original write funcition"""
          ....
      def MyWrite(self, fp):
          """Define new function and inherit all others"""

But, shouldn’t I call init? Is this in Python 2 the equivalent:

 class AmritaConfigParser(ConfigParser.SafeConfigParser):
    #def __init__(self):
    #    super().__init__() # Python3 syntax, or rather, new style class syntax ...
    #
    # is this the equivalent of the above ? 
    def __init__(self):
        ConfigParser.SafeConfigParser.__init__(self)

回答 0

  • super()(不带参数)在Python 3中(以及__class__)引入:

    super() -> same as super(__class__, self)

    这样就相当于新样式类的Python 2:

    super(CurrentClass, self)
  • 对于老式类,您可以始终使用:

     class Classname(OldStyleParent):
        def __init__(self, *args, **kwargs):
            OldStyleParent.__init__(self, *args, **kwargs)
  • super() (without arguments) was introduced in Python 3 (along with __class__):

    super() -> same as super(__class__, self)
    

    so that would be the Python 2 equivalent for new-style classes:

    super(CurrentClass, self)
    
  • for old-style classes you can always use:

     class Classname(OldStyleParent):
        def __init__(self, *args, **kwargs):
            OldStyleParent.__init__(self, *args, **kwargs)
    

回答 1

在单个继承的情况下(仅当子类化一个类时),新类将继承基类的方法。这包括__init__。因此,如果您不在课堂上定义它,那么您将从基础中获得一个。

如果引入多重继承(一次子类化多个类),事情就会变得复杂起来。这是因为如果有多个基类__init__,则您的类将仅继承第一个基类。

在这种情况下,super如果可以的话,您应该真正使用,我会解释原因。但并非总是可以。问题是您所有的基类也必须使用它(以及它们的基类-整个树)。

如果是这种情况,那么这也将正常工作(在Python 3中,但您可以将其重新制作为Python 2-它还具有super):

class A:
    def __init__(self):
        print('A')
        super().__init__()

class B:
    def __init__(self):
        print('B')
        super().__init__()

class C(A, B):
    pass

C()
#prints:
#A
#B

请注意,super即使它们没有自己的基类,这两个基类也如何使用。

什么super做的是:它要求从隔壁班的MRO方法(方法解析顺序)。的MRO为C(C, A, B, object)。您可以打印C.__mro__以查看它。

因此,C继承__init__自调用AsuperA.__init__调用中继承B.__init__(在MRO中B遵循A)。

因此,通过不执行任何操作C,最终会同时调用这两者,而这正是您想要的。

现在,如果您不使用super,您将最终继承A.__init__(与以前一样),但是这次没有什么B.__init__需要您使用。

class A:
    def __init__(self):
        print('A')

class B:
    def __init__(self):
        print('B')

class C(A, B):
    pass

C()
#prints:
#A

要解决此问题,您必须定义C.__init__

class C(A, B):
    def __init__(self):
        A.__init__(self)
        B.__init__(self)

这样做的问题是,在更复杂的MI树中,__init__某些类的方法最终可能会被多次调用,而super / MRO保证只将它们调用一次。

In a single inheritance case (when you subclass one class only), your new class inherits methods of the base class. This includes __init__. So if you don’t define it in your class, you will get the one from the base.

Things start being complicated if you introduce multiple inheritance (subclassing more than one class at a time). This is because if more than one base class has __init__, your class will inherit the first one only.

In such cases, you should really use super if you can, I’ll explain why. But not always you can. The problem is that all your base classes must also use it (and their base classes as well — the whole tree).

If that is the case, then this will also work correctly (in Python 3 but you could rework it into Python 2 — it also has super):

class A:
    def __init__(self):
        print('A')
        super().__init__()

class B:
    def __init__(self):
        print('B')
        super().__init__()

class C(A, B):
    pass

C()
#prints:
#A
#B

Notice how both base classes use super even though they don’t have their own base classes.

What super does is: it calls the method from the next class in MRO (method resolution order). The MRO for C is: (C, A, B, object). You can print C.__mro__ to see it.

So, C inherits __init__ from A and super in A.__init__ calls B.__init__ (B follows A in MRO).

So by doing nothing in C, you end up calling both, which is what you want.

Now if you were not using super, you would end up inheriting A.__init__ (as before) but this time there’s nothing that would call B.__init__ for you.

class A:
    def __init__(self):
        print('A')

class B:
    def __init__(self):
        print('B')

class C(A, B):
    pass

C()
#prints:
#A

To fix that you have to define C.__init__:

class C(A, B):
    def __init__(self):
        A.__init__(self)
        B.__init__(self)

The problem with that is that in more complicated MI trees, __init__ methods of some classes may end up being called more than once whereas super/MRO guarantee that they’re called just once.


回答 2

简而言之,它们是等效的。让我们来看看历史:

(1)首先,函数看起来像这样。

    class MySubClass(MySuperClass):
        def __init__(self):
            MySuperClass.__init__(self)

(2)使代码更抽象(更便于移植)。发明超类的一种常见方法是:

    super(<class>, <instance>)

初始化函数可以是:

    class MySubClassBetter(MySuperClass):
        def __init__(self):
            super(MySubClassBetter, self).__init__()

但是,要求显式传递类和实例都违反了DRY(请勿重复自己)规则。

(3)在V3中。更聪明

    super()

在大多数情况下就足够了。您可以参考http://www.python.org/dev/peps/pep-3135/

In short, they are equivalent. Let’s have a history view:

(1) at first, the function looks like this.

    class MySubClass(MySuperClass):
        def __init__(self):
            MySuperClass.__init__(self)

(2) to make code more abstract (and more portable). A common method to get Super-Class is invented like:

    super(<class>, <instance>)

And init function can be:

    class MySubClassBetter(MySuperClass):
        def __init__(self):
            super(MySubClassBetter, self).__init__()

However requiring an explicit passing of both the class and instance break the DRY (Don’t Repeat Yourself) rule a bit.

(3) in V3. It is more smart,

    super()

is enough in most case. You can refer to http://www.python.org/dev/peps/pep-3135/


回答 3

只是为Python 3提供一个简单而完整的示例,大多数人似乎正在使用它。

class MySuper(object):
    def __init__(self,a):
        self.a = a

class MySub(MySuper):
    def __init__(self,a,b):
        self.b = b
        super().__init__(a)

my_sub = MySub(42,'chickenman')
print(my_sub.a)
print(my_sub.b)

42
chickenman

Just to have a simple and complete example for Python 3, which most people seem to be using now.

class MySuper(object):
    def __init__(self,a):
        self.a = a

class MySub(MySuper):
    def __init__(self,a,b):
        self.b = b
        super().__init__(a)

my_sub = MySub(42,'chickenman')
print(my_sub.a)
print(my_sub.b)

gives

42
chickenman

回答 4

另一个python3实现,其中涉及将Abstract类与super()结合使用。你应该记住

super().__init__(name, 10)

与…具有相同的效果

Person.__init__(self, name, 10)

请记住,super()中有一个隐藏的“自我”,因此同一对象会传递给超类init方法,并且属性会添加到调用它的对象中。因此super()被翻译为 Person,然后如果您包含隐藏的自身,则会得到上面的代码片段。

from abc import ABCMeta, abstractmethod
class Person(metaclass=ABCMeta):
    name = ""
    age = 0

    def __init__(self, personName, personAge):
        self.name = personName
        self.age = personAge

    @abstractmethod
    def showName(self):
        pass

    @abstractmethod
    def showAge(self):
        pass


class Man(Person):

    def __init__(self, name, height):
        self.height = height
        # Person.__init__(self, name, 10)
        super().__init__(name, 10)  # same as Person.__init__(self, name, 10)
        # basically used to call the superclass init . This is used incase you want to call subclass init
        # and then also call superclass's init.
        # Since there's a hidden self in the super's parameters, when it's is called,
        # the superclasses attributes are a part of the same object that was sent out in the super() method

    def showIdentity(self):
        return self.name, self.age, self.height

    def showName(self):
        pass

    def showAge(self):
        pass


a = Man("piyush", "179")
print(a.showIdentity())

Another python3 implementation that involves the use of Abstract classes with super(). You should remember that

super().__init__(name, 10)

has the same effect as

Person.__init__(self, name, 10)

Remember there’s a hidden ‘self’ in super(), So the same object passes on to the superclass init method and the attributes are added to the object that called it. Hence super()gets translated to Person and then if you include the hidden self, you get the above code frag.

from abc import ABCMeta, abstractmethod
class Person(metaclass=ABCMeta):
    name = ""
    age = 0

    def __init__(self, personName, personAge):
        self.name = personName
        self.age = personAge

    @abstractmethod
    def showName(self):
        pass

    @abstractmethod
    def showAge(self):
        pass


class Man(Person):

    def __init__(self, name, height):
        self.height = height
        # Person.__init__(self, name, 10)
        super().__init__(name, 10)  # same as Person.__init__(self, name, 10)
        # basically used to call the superclass init . This is used incase you want to call subclass init
        # and then also call superclass's init.
        # Since there's a hidden self in the super's parameters, when it's is called,
        # the superclasses attributes are a part of the same object that was sent out in the super() method

    def showIdentity(self):
        return self.name, self.age, self.height

    def showName(self):
        pass

    def showAge(self):
        pass


a = Man("piyush", "179")
print(a.showIdentity())

ConfigParser中的列表

问题:ConfigParser中的列表

典型的ConfigParser生成的文件如下所示:

[Section]
bar=foo
[Section 2]
bar2= baz

现在,有一种方法可以索引列表,例如:

[Section 3]
barList={
    item1,
    item2
}

相关问题:每节Python的ConfigParser唯一键

The typical ConfigParser generated file looks like:

[Section]
bar=foo
[Section 2]
bar2= baz

Now, is there a way to index lists like, for instance:

[Section 3]
barList={
    item1,
    item2
}

Related question: Python’s ConfigParser unique keys per section


回答 0

没有什么可以阻止您将列表打包为定界字符串,然后从配置中获取字符串后再将其解包。如果您采用这种方式,则您的config部分如下所示:

[Section 3]
barList=item1,item2

它虽然不漂亮,但对于大多数简单列表来说都可以使用。

There is nothing stopping you from packing the list into a delimited string and then unpacking it once you get the string from the config. If you did it this way your config section would look like:

[Section 3]
barList=item1,item2

It’s not pretty but it’s functional for most simple lists.


回答 1

也有点晚,但可能对某些人有所帮助。我正在使用ConfigParser和JSON的组合:

[Foo]
fibs: [1,1,2,3,5,8,13]

只需阅读以下内容:

>>> json.loads(config.get("Foo","fibs"))
[1, 1, 2, 3, 5, 8, 13]

如果列表很长,您甚至可以换行(感谢@ peter-smit):

[Bar]
files_to_check = [
     "/path/to/file1",
     "/path/to/file2",
     "/path/to/another file with space in the name"
     ]

当然,我可以只使用JSON,但是我发现配置文件更具可读性,并且[DEFAULT]节非常方便。

Also a bit late, but maybe helpful for some. I am using a combination of ConfigParser and JSON:

[Foo]
fibs: [1,1,2,3,5,8,13]

just read it with:

>>> json.loads(config.get("Foo","fibs"))
[1, 1, 2, 3, 5, 8, 13]

You can even break lines if your list is long (thanks @peter-smit):

[Bar]
files_to_check = [
     "/path/to/file1",
     "/path/to/file2",
     "/path/to/another file with space in the name"
     ]

Of course i could just use JSON, but i find config files much more readable, and the [DEFAULT] Section very handy.


回答 2

这次聚会来晚了,但是我最近在配置文件中的专用部分实现了此功能:

[paths]
path1           = /some/path/
path2           = /another/path/
...

config.items( "paths" )用于获取路径项的可迭代列表,如下所示:

path_items = config.items( "paths" )
for key, path in path_items:
    #do something with path

希望这可以帮助其他人谷歌搜索这个问题;)

Coming late to this party, but I recently implemented this with a dedicated section in a config file for a list:

[paths]
path1           = /some/path/
path2           = /another/path/
...

and using config.items( "paths" ) to get an iterable list of path items, like so:

path_items = config.items( "paths" )
for key, path in path_items:
    #do something with path

Hope this helps other folk Googling this question ;)


回答 3

很多人不知道的一件事是允许多行配置值。例如:

;test.ini
[hello]
barlist = 
    item1
    item2

现在的值config.get('hello','barlist')将是:

"\nitem1\nitem2"

您可以使用splitlines方法轻松拆分(不要忘记过滤空项目)。

如果我们查看像Pyramid这样的大型框架,他们将使用以下技术:

def aslist_cronly(value):
    if isinstance(value, string_types):
        value = filter(None, [x.strip() for x in value.splitlines()])
    return list(value)

def aslist(value, flatten=True):
    """ Return a list of strings, separating the input based on newlines
    and, if flatten=True (the default), also split on spaces within
    each line."""
    values = aslist_cronly(value)
    if not flatten:
        return values
    result = []
    for value in values:
        subvalues = value.split()
        result.extend(subvalues)
    return result

资源

我自己,如果这对您来说很常见,我可能会扩展ConfigParser:

class MyConfigParser(ConfigParser):
    def getlist(self,section,option):
        value = self.get(section,option)
        return list(filter(None, (x.strip() for x in value.splitlines())))

    def getlistint(self,section,option):
        return [int(x) for x in self.getlist(section,option)]

请注意,使用此技术时需要注意一些事项

  1. 作为项目的新行应以空格开头(例如,空格或制表符)
  2. 以下所有以空格开头的行均被视为上一项的一部分。同样,如果它带有=号或以;开头 跟随空白。

One thing a lot of people don’t know is that multi-line configuration-values are allowed. For example:

;test.ini
[hello]
barlist = 
    item1
    item2

The value of config.get('hello','barlist') will now be:

"\nitem1\nitem2"

Which you easily can split with the splitlines method (don’t forget to filter empty items).

If we look to a big framework like Pyramid they are using this technique:

def aslist_cronly(value):
    if isinstance(value, string_types):
        value = filter(None, [x.strip() for x in value.splitlines()])
    return list(value)

def aslist(value, flatten=True):
    """ Return a list of strings, separating the input based on newlines
    and, if flatten=True (the default), also split on spaces within
    each line."""
    values = aslist_cronly(value)
    if not flatten:
        return values
    result = []
    for value in values:
        subvalues = value.split()
        result.extend(subvalues)
    return result

Source

Myself, I would maybe extend the ConfigParser if this is a common thing for you:

class MyConfigParser(ConfigParser):
    def getlist(self,section,option):
        value = self.get(section,option)
        return list(filter(None, (x.strip() for x in value.splitlines())))

    def getlistint(self,section,option):
        return [int(x) for x in self.getlist(section,option)]

Note that there are a few things to look out for when using this technique

  1. New lines that are items should start with whitespace (e.g. a space or a tab)
  2. All following lines that start with whitespace are considered to be part of the previous item. Also if it has an = sign or if it starts with a ; following the whitespace.

回答 4

如果您想逐字传递列表,则可以使用:

ast.literal_eval()

例如配置:

[section]
option=["item1","item2","item3"]

代码是:

import ConfigParser
import ast

my_list = ast.literal_eval(config.get("section", "option"))
print(type(my_list))
print(my_list)

输出:

<type'list'>
["item1","item2","item3"]

If you want to literally pass in a list then you can use:

ast.literal_eval()

For example configuration:

[section]
option=["item1","item2","item3"]

The code is:

import ConfigParser
import ast

my_list = ast.literal_eval(config.get("section", "option"))
print(type(my_list))
print(my_list)

output:

<type'list'>
["item1","item2","item3"]

回答 5

没有提及converterskwargConfigParser()这些答案中是令人失望的。

根据文档,您可以将字典传递给该字典ConfigParser,以添加一个get解析器和节代理方法。因此,对于列表:

example.ini

[Germ]
germs: a,list,of,names, and,1,2, 3,numbers

解析器示例:

cp = ConfigParser(converters={'list': lambda x: [i.strip() for i in x.split(',')]})
cp.read('example.ini')
cp.getlist('Germ', 'germs')
['a', 'list', 'of', 'names', 'and', '1', '2', '3', 'numbers']
cp['Germ'].getlist('germs')
['a', 'list', 'of', 'names', 'and', '1', '2', '3', 'numbers']

这是我个人的最爱,因为不需要子类化,而且我不必依靠最终用户来完美地编写JSON或可由解释的列表ast.literal_eval

No mention of the converters kwarg for ConfigParser() in any of these answers was rather disappointing.

According to the documentation you can pass a dictionary to ConfigParser that will add a get method for both the parser and section proxies. So for a list:

example.ini

[Germ]
germs: a,list,of,names, and,1,2, 3,numbers

Parser example:

cp = ConfigParser(converters={'list': lambda x: [i.strip() for i in x.split(',')]})
cp.read('example.ini')
cp.getlist('Germ', 'germs')
['a', 'list', 'of', 'names', 'and', '1', '2', '3', 'numbers']
cp['Germ'].getlist('germs')
['a', 'list', 'of', 'names', 'and', '1', '2', '3', 'numbers']

This is my personal favorite as no subclassing is necessary and I don’t have to rely on an end user to perfectly write JSON or a list that can be interpreted by ast.literal_eval.


回答 6

我降落在这里试图消耗这个…

[global]
spys = richard.sorge@cccp.gov, mata.hari@deutschland.gov

答案是在逗号上将其拆分并去除空格:

SPYS = [e.strip() for e in parser.get('global', 'spys').split(',')]

要获得列表结果:

['richard.sorge@cccp.gov', 'mata.hari@deutschland.gov']

它可能无法完全回答OP的问题,但可能是某些人正在寻找的简单答案。

I landed here seeking to consume this…

[global]
spys = richard.sorge@cccp.gov, mata.hari@deutschland.gov

The answer is to split it on the comma and strip the spaces:

SPYS = [e.strip() for e in parser.get('global', 'spys').split(',')]

To get a list result:

['richard.sorge@cccp.gov', 'mata.hari@deutschland.gov']

It may not answer the OP’s question exactly but might be the simple answer some people are looking for.


回答 7

这是我用于列表的内容:

配置文件内容:

[sect]
alist = a
        b
        c

代码:

l = config.get('sect', 'alist').split('\n')

它适用于弦乐

如果是数字

配置内容:

nlist = 1
        2
        3

码:

nl = config.get('sect', 'alist').split('\n')
l = [int(nl) for x in nl]

谢谢。

This is what I use for lists:

config file content:

[sect]
alist = a
        b
        c

code :

l = config.get('sect', 'alist').split('\n')

it work for strings

in case of numbers

config content:

nlist = 1
        2
        3

code:

nl = config.get('sect', 'alist').split('\n')
l = [int(nl) for x in nl]

thanks.


回答 8

因此,我更喜欢的另一种方法是只拆分值,例如:

#/path/to/config.cfg
[Numbers]
first_row = 1,2,4,8,12,24,36,48

可以像这样将其加载到字符串或整数列表中,如下所示:

import configparser

config = configparser.ConfigParser()
config.read('/path/to/config.cfg')

# Load into a list of strings
first_row_strings = config.get('Numbers', 'first_row').split(',')

# Load into a list of integers
first_row_integers = [int(x) for x in config.get('Numbers', 'first_row').split(',')]

此方法避免您需要将值包装在方括号中以作为JSON加载。

So another way, which I prefer, is to just split the values, for example:

#/path/to/config.cfg
[Numbers]
first_row = 1,2,4,8,12,24,36,48

Could be loaded like this into a list of strings or integers, as follows:

import configparser

config = configparser.ConfigParser()
config.read('/path/to/config.cfg')

# Load into a list of strings
first_row_strings = config.get('Numbers', 'first_row').split(',')

# Load into a list of integers
first_row_integers = [int(x) for x in config.get('Numbers', 'first_row').split(',')]

This method prevents you from needing to wrap your values in brackets to load as JSON.


回答 9

配置解析器仅支持对原始类型进行序列化。我将使用JSON或YAML来满足这种要求。

Only primitive types are supported for serialization by config parser. I would use JSON or YAML for that kind of requirement.


回答 10

我过去也遇到过同样的问题。如果需要更复杂的列表,请考虑通过从ConfigParser继承来创建自己的解析器。然后,您将用此方法覆盖get方法:

    def get(self, section, option):
    """ Get a parameter
    if the returning value is a list, convert string value to a python list"""
    value = SafeConfigParser.get(self, section, option)
    if (value[0] == "[") and (value[-1] == "]"):
        return eval(value)
    else:
        return value

使用此解决方案,您还可以在配置文件中定义字典。

但小心点!这不是那么安全:这意味着任何人都可以通过您的配置文件运行代码。如果在您的项目中安全性不是问题,我会考虑直接使用python类作为配置文件。与ConfigParser文件相比,以下文件更强大,更可消耗:

class Section
    bar = foo
class Section2
    bar2 = baz
class Section3
    barList=[ item1, item2 ]

I faced the same problem in the past. If you need more complex lists, consider creating your own parser by inheriting from ConfigParser. Then you would overwrite the get method with that:

    def get(self, section, option):
    """ Get a parameter
    if the returning value is a list, convert string value to a python list"""
    value = SafeConfigParser.get(self, section, option)
    if (value[0] == "[") and (value[-1] == "]"):
        return eval(value)
    else:
        return value

With this solution you will also be able to define dictionaries in your config file.

But be careful! This is not as safe: this means anyone could run code through your config file. If security is not an issue in your project, I would consider using directly python classes as config files. The following is much more powerful and expendable than a ConfigParser file:

class Section
    bar = foo
class Section2
    bar2 = baz
class Section3
    barList=[ item1, item2 ]

回答 11

import ConfigParser
import os

class Parser(object):
    """attributes may need additional manipulation"""
    def __init__(self, section):
        """section to retun all options on, formatted as an object
        transforms all comma-delimited options to lists
        comma-delimited lists with colons are transformed to dicts
        dicts will have values expressed as lists, no matter the length
        """
        c = ConfigParser.RawConfigParser()
        c.read(os.path.join(os.path.dirname(__file__), 'config.cfg'))

        self.section_name = section

        self.__dict__.update({k:v for k, v in c.items(section)})

        #transform all ',' into lists, all ':' into dicts
        for key, value in self.__dict__.items():
            if value.find(':') > 0:
                #dict
                vals = value.split(',')
                dicts = [{k:v} for k, v in [d.split(':') for d in vals]]
                merged = {}
                for d in dicts:
                    for k, v in d.items():
                        merged.setdefault(k, []).append(v)
                self.__dict__[key] = merged
            elif value.find(',') > 0:
                #list
                self.__dict__[key] = value.split(',')

所以现在我的config.cfg文件可能看起来像这样:

[server]
credentials=username:admin,password:$3<r3t
loggingdirs=/tmp/logs,~/logs,/var/lib/www/logs
timeoutwait=15

可以解析为我的小型项目的足够细的对象。

>>> import config
>>> my_server = config.Parser('server')
>>> my_server.credentials
{'username': ['admin'], 'password', ['$3<r3t']}
>>> my_server.loggingdirs:
['/tmp/logs', '~/logs', '/var/lib/www/logs']
>>> my_server.timeoutwait
'15'

这是用于非常简单的配置的快速解析,您将失去获取int,bool和其他类型的输出的所有功能,而无需转换从返回的对象Parser或重新执行由Parser类在其他地方完成的解析工作。

import ConfigParser
import os

class Parser(object):
    """attributes may need additional manipulation"""
    def __init__(self, section):
        """section to retun all options on, formatted as an object
        transforms all comma-delimited options to lists
        comma-delimited lists with colons are transformed to dicts
        dicts will have values expressed as lists, no matter the length
        """
        c = ConfigParser.RawConfigParser()
        c.read(os.path.join(os.path.dirname(__file__), 'config.cfg'))

        self.section_name = section

        self.__dict__.update({k:v for k, v in c.items(section)})

        #transform all ',' into lists, all ':' into dicts
        for key, value in self.__dict__.items():
            if value.find(':') > 0:
                #dict
                vals = value.split(',')
                dicts = [{k:v} for k, v in [d.split(':') for d in vals]]
                merged = {}
                for d in dicts:
                    for k, v in d.items():
                        merged.setdefault(k, []).append(v)
                self.__dict__[key] = merged
            elif value.find(',') > 0:
                #list
                self.__dict__[key] = value.split(',')

So now my config.cfg file, which could look like this:

[server]
credentials=username:admin,password:$3<r3t
loggingdirs=/tmp/logs,~/logs,/var/lib/www/logs
timeoutwait=15

Can be parsed into fine-grained-enough objects for my small project.

>>> import config
>>> my_server = config.Parser('server')
>>> my_server.credentials
{'username': ['admin'], 'password', ['$3<r3t']}
>>> my_server.loggingdirs:
['/tmp/logs', '~/logs', '/var/lib/www/logs']
>>> my_server.timeoutwait
'15'

This is for very quick parsing of simple configs, you lose all ability to fetch ints, bools, and other types of output without either transforming the object returned from Parser, or re-doing the parsing job accomplished by the Parser class elsewhere.


回答 12

我在项目中用没有值的键完成了类似的任务:

import configparser

# allow_no_value param says that no value keys are ok
config = configparser.ConfigParser(allow_no_value=True)

# overwrite optionxform method for overriding default behaviour (I didn't want lowercased keys)
config.optionxform = lambda optionstr: optionstr

config.read('./app.config')

features = list(config['FEATURES'].keys())

print(features)

输出:

['BIOtag', 'TextPosition', 'IsNoun', 'IsNomn']

app.config:

[FEATURES]
BIOtag
TextPosition
IsNoun
IsNomn

I completed similar task in my project with section with keys without values:

import configparser

# allow_no_value param says that no value keys are ok
config = configparser.ConfigParser(allow_no_value=True)

# overwrite optionxform method for overriding default behaviour (I didn't want lowercased keys)
config.optionxform = lambda optionstr: optionstr

config.read('./app.config')

features = list(config['FEATURES'].keys())

print(features)

Output:

['BIOtag', 'TextPosition', 'IsNoun', 'IsNomn']

app.config:

[FEATURES]
BIOtag
TextPosition
IsNoun
IsNomn

回答 13

json.loadsast.literal_eval似乎有效,但config中的简单列表将每个字符视为字节,因此甚至返回方括号…。

意思是如果config有 fieldvalue = [1,2,3,4,5]

然后config.read(*.cfg) config['fieldValue'][0]返回[代替1

json.loads & ast.literal_eval seems to be working but simple list within config is treating each character as byte so returning even square bracket….

meaning if config has fieldvalue = [1,2,3,4,5]

then config.read(*.cfg) config['fieldValue'][0] returning [ in place of 1


回答 14

如Peter Smit(https://stackoverflow.com/a/11866695/7424596)所述,您可能想扩展ConfigParser,此外,可以使用Interpolator自动在列表中进行转换。

作为参考,您可以在下面找到自动转换配置的代码:

[DEFAULT]
keys = [
    Overall cost structure, Capacity, RAW MATERIALS,
    BY-PRODUCT CREDITS, UTILITIES, PLANT GATE COST,
    PROCESS DESCRIPTION, AT 50% CAPACITY, PRODUCTION COSTS,
    INVESTMENT, US$ MILLION, PRODUCTION COSTS, US ¢/LB,
    VARIABLE COSTS, PRODUCTION COSTS, MAINTENANCE MATERIALS
  ]

因此,如果您请求密钥,您将获得:

<class 'list'>: ['Overall cost structure', 'Capacity', 'RAW MATERIALS', 'BY-PRODUCT CREDITS', 'UTILITIES', 'PLANT GATE COST', 'PROCESS DESCRIPTION', 'AT 50% CAPACITY', 'PRODUCTION COSTS', 'INVESTMENT', 'US$ MILLION', 'PRODUCTION COSTS', 'US ¢/LB', 'VARIABLE COSTS', 'PRODUCTION COSTS', 'MAINTENANCE MATERIALS']

码:

class AdvancedInterpolator(Interpolation):
    def before_get(self, parser, section, option, value, defaults):
        is_list = re.search(parser.LIST_MATCHER, value)
        if is_list:
            return parser.getlist(section, option, raw=True)
        return value


class AdvancedConfigParser(ConfigParser):

    _DEFAULT_INTERPOLATION = AdvancedInterpolator()

    LIST_SPLITTER = '\s*,\s*'
    LIST_MATCHER = '^\[([\s\S]*)\]$'

    def _to_list(self, str):
        is_list = re.search(self.LIST_MATCHER, str)
        if is_list:
            return re.split(self.LIST_SPLITTER, is_list.group(1))
        else:
            return re.split(self.LIST_SPLITTER, str)


    def getlist(self, section, option, conv=lambda x:x.strip(), *, raw=False, vars=None,
                  fallback=_UNSET, **kwargs):
        return self._get_conv(
                section, option,
                lambda value: [conv(x) for x in self._to_list(value)],
                raw=raw,
                vars=vars,
                fallback=fallback,
                **kwargs
        )

    def getlistint(self, section, option, *, raw=False, vars=None,
            fallback=_UNSET, **kwargs):
        return self.getlist(section, option, int, raw=raw, vars=vars,
                fallback=fallback, **kwargs)

    def getlistfloat(self, section, option, *, raw=False, vars=None,
            fallback=_UNSET, **kwargs):
        return self.getlist(section, option, float, raw=raw, vars=vars,
                fallback=fallback, **kwargs)

    def getlistboolean(self, section, option, *, raw=False, vars=None,
            fallback=_UNSET, **kwargs):
        return self.getlist(section, option, self._convert_to_boolean,
                raw=raw, vars=vars, fallback=fallback, **kwargs)

聚苯乙烯记住缩进的重要性。如在ConfigParser文档字符串中读取的:

值可以缩进多行,只要缩进的深度比值的第一行深即可。根据解析器的模式,空白行可能被视为多行值的一部分或被忽略。

As mentioned by Peter Smit (https://stackoverflow.com/a/11866695/7424596) You might want to extend ConfigParser, in addition, an Interpolator can be used to automatically convert into and from the list.

For reference at the bottom you can find code which automatically converts config like:

[DEFAULT]
keys = [
    Overall cost structure, Capacity, RAW MATERIALS,
    BY-PRODUCT CREDITS, UTILITIES, PLANT GATE COST,
    PROCESS DESCRIPTION, AT 50% CAPACITY, PRODUCTION COSTS,
    INVESTMENT, US$ MILLION, PRODUCTION COSTS, US ¢/LB,
    VARIABLE COSTS, PRODUCTION COSTS, MAINTENANCE MATERIALS
  ]

So if you request keys you will get:

<class 'list'>: ['Overall cost structure', 'Capacity', 'RAW MATERIALS', 'BY-PRODUCT CREDITS', 'UTILITIES', 'PLANT GATE COST', 'PROCESS DESCRIPTION', 'AT 50% CAPACITY', 'PRODUCTION COSTS', 'INVESTMENT', 'US$ MILLION', 'PRODUCTION COSTS', 'US ¢/LB', 'VARIABLE COSTS', 'PRODUCTION COSTS', 'MAINTENANCE MATERIALS']

Code:

class AdvancedInterpolator(Interpolation):
    def before_get(self, parser, section, option, value, defaults):
        is_list = re.search(parser.LIST_MATCHER, value)
        if is_list:
            return parser.getlist(section, option, raw=True)
        return value


class AdvancedConfigParser(ConfigParser):

    _DEFAULT_INTERPOLATION = AdvancedInterpolator()

    LIST_SPLITTER = '\s*,\s*'
    LIST_MATCHER = '^\[([\s\S]*)\]$'

    def _to_list(self, str):
        is_list = re.search(self.LIST_MATCHER, str)
        if is_list:
            return re.split(self.LIST_SPLITTER, is_list.group(1))
        else:
            return re.split(self.LIST_SPLITTER, str)


    def getlist(self, section, option, conv=lambda x:x.strip(), *, raw=False, vars=None,
                  fallback=_UNSET, **kwargs):
        return self._get_conv(
                section, option,
                lambda value: [conv(x) for x in self._to_list(value)],
                raw=raw,
                vars=vars,
                fallback=fallback,
                **kwargs
        )

    def getlistint(self, section, option, *, raw=False, vars=None,
            fallback=_UNSET, **kwargs):
        return self.getlist(section, option, int, raw=raw, vars=vars,
                fallback=fallback, **kwargs)

    def getlistfloat(self, section, option, *, raw=False, vars=None,
            fallback=_UNSET, **kwargs):
        return self.getlist(section, option, float, raw=raw, vars=vars,
                fallback=fallback, **kwargs)

    def getlistboolean(self, section, option, *, raw=False, vars=None,
            fallback=_UNSET, **kwargs):
        return self.getlist(section, option, self._convert_to_boolean,
                raw=raw, vars=vars, fallback=fallback, **kwargs)

Ps keep in mind importance of indentdation. As reads in ConfigParser doc string:

Values can span multiple lines, as long as they are indented deeper than the first line of the value. Depending on the parser’s mode, blank lines may be treated as parts of multiline values or ignored.