Danh sách trong ConfigParser


Câu trả lời:


142

Không có gì ngăn bạn đóng gói danh sách thành một chuỗi phân tách và sau đó giải nén nó sau khi bạn nhận được chuỗi từ cấu hình. Nếu bạn đã làm theo cách này, phần cấu hình của bạn sẽ như sau:

[Section 3]
barList=item1,item2

Nó không đẹp nhưng nó hoạt động với hầu hết các danh sách đơn giản.


2
Và nếu bạn có danh sách phức tạp, bạn có thể tham khảo câu hỏi này: stackoverflow.com/questions/330900/iêu :-)
John Fouhy

Giải pháp tốt, nhưng làm thế nào để làm điều đó nếu không có dấu phân cách có thể hơn bạn có thể đảm bảo sẽ không xuất hiện trong một mục danh sách ???
wim

@wim Xem câu trả lời của tôi, bạn có thể sử dụng \ n làm dấu phân cách
Peter Smit

@wim Bạn sẽ cần phải thực hiện một cách để thoát khỏi ký tự phân cách nếu nó có thể là một ký tự hợp pháp. (Và một cách để thoát khỏi bất kỳ nhân vật nào bạn sử dụng để trốn thoát.)
jamesdlin

Điều gì nếu một danh sách có một yếu tố duy nhất?
Sérgio Mafra

223

Cũng hơi muộn, nhưng có thể hữu ích cho một số. Tôi đang sử dụng kết hợp ConfigParser và JSON:

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

chỉ cần đọc nó với:

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

Bạn thậm chí có thể ngắt dòng nếu danh sách của bạn dài (cảm ơn @ peter-smit):

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

Tất nhiên tôi chỉ có thể sử dụng JSON, nhưng tôi thấy các tệp cấu hình dễ đọc hơn nhiều và Phần [DEFAULT] rất tiện dụng.


1
Thật tuyệt vời vì nó tự động "bỏ" các giá trị có thể hữu ích nếu bạn không biết trước các loại.
LeGBT

Tôi thích ý tưởng này, nhưng tôi chỉ có thể làm cho nó hoạt động với danh sách các số. Dấu ngoặc kép không giúp đỡ. Kỳ dị. Tiến lên.
rsaw

5
Bạn sẽ phải có ["a", "b", "c"] để các chuỗi hoạt động. Đối với tôi, lần nhấp này cho các số nhưng vì các tệp cfg hầu hết có thể chỉnh sửa - thêm "" mọi lúc là một nỗi đau. Tôi thà sử dụng dấu phẩy và sau đó tách nó ra.
Saurabh Hirani 16/2/2015

Một giải pháp thanh lịch chỉ sử dụng thư viện tiêu chuẩn. Rất vui được sử dụng bình luận và json.
wi1

Làm thế nào điều này sẽ làm việc cho chuỗi thô, ví dụ key5 : [r"abc $x_i$", r"def $y_j$"]? Họ nêu ra lỗijson.decoder.JSONDecodeError: Expecting value: line 1 column 2 (char 1)
kingusiu

101

Đến muộn với bữa tiệc này, nhưng gần đây tôi đã thực hiện điều này với một phần dành riêng trong tệp cấu hình cho danh sách:

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

và sử dụng config.items( "paths" )để có được một danh sách lặp lại các mục đường dẫn, như vậy:

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

Hy vọng điều này sẽ giúp những người khác hiểu được câu hỏi này;)


3
Tôi thích giải pháp này, bởi vì bạn có thể ; commentra một số mục nhất định từ danh sách mà không phải viết lại toàn bộ danh sách.
wim

1
+1, nhưng nếu bạn làm điều này, chỉ cần cẩn thận với việc sử dụng key, vì ConfigParser chuyển đổi tất cả các khóa như vậy thành chữ thường
Alex Dean

4
@AlexDean Bạn có thể thiết lập ConfigParser để đặt camelCase vào vị trí bằng cách đặt tùy chọnxform = str. Ví dụ: config = ConfigParser.SafeConfigParser() config.optionxform = str Sau đó, vụ việc sẽ được để lại một mình
Cameron Goodale

@Henry Cooke Bạn đã kiểm tra rằng khi một khóa được liệt kê nhiều lần chưa?
DevPlayer

1
@DevPlayer Với cách sử dụng đa khóa, bạn chỉ nhận được giá trị cuối cùng. (trả lời bình luận cũ 2 năm vì lợi ích của những độc giả khác)
Marcin K

63

Một điều mà nhiều người không biết là giá trị cấu hình nhiều dòng được cho phép. Ví dụ:

;test.ini
[hello]
barlist = 
    item1
    item2

Giá trị của config.get('hello','barlist')ý chí bây giờ là:

"\nitem1\nitem2"

Mà bạn dễ dàng có thể phân chia bằng phương pháp splitlines (đừng quên lọc các mục trống).

Nếu chúng ta tìm đến một khung lớn như Kim tự tháp thì họ đang sử dụng kỹ thuật này:

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

Nguồn

Bản thân tôi, tôi có thể sẽ mở rộng Trình cấu hình nếu đây là điều phổ biến đối với bạn:

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)]

Lưu ý rằng có một số điều cần chú ý khi sử dụng kỹ thuật này

  1. Các dòng mới là các mục nên bắt đầu bằng khoảng trắng (ví dụ: khoảng trắng hoặc tab)
  2. Tất cả các dòng sau bắt đầu bằng khoảng trắng được coi là một phần của mục trước. Ngoài ra nếu nó có dấu = hoặc nếu bắt đầu bằng a; theo khoảng trắng.

Tại sao bạn sử dụng .splitlines()thay vì .split()? Sử dụng hành vi mặc định của từng, phân chia rõ ràng là vượt trội (lọc ra các dòng trống). Trừ khi tôi thiếu thứ gì đó ...
rsaw

7
.split () ngắt trên tất cả các khoảng trắng (trừ khi có một ký tự cụ thể), .splitlines () ngắt trên tất cả các ký tự dòng mới.
Peter Smit

Ahhh điểm tốt. Tôi đã không nghĩ về điều đó vì không có giá trị nào của tôi có khoảng trống.
rsaw

38

Nếu bạn muốn vượt qua trong danh sách theo nghĩa đen thì bạn có thể sử dụng:

ast.literal_eval()

Ví dụ cấu hình:

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

Mã này là:

import ConfigParser
import ast

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

đầu ra:

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

Trong trường hợp này, lợi thế của việc sử dụng ast.literal_eval()khi so sánh để sử dụng (được cho là phổ biến hơn) là json.loads()gì? Tôi nghĩ rằng sau này cung cấp bảo mật hơn, không?
RayLuo

2
Tôi rất thích xem và ví dụ về điều này, vui lòng thêm câu trả lời cho chủ đề này nếu bạn cảm thấy nó có ích, mặc dù nhận xét của bạn sẽ tự đặt ra một câu hỏi hay. Câu trả lời tôi đã đơn giản hóa việc tiêu thụ danh sách từ ConfigParser, do đó, bên trong ứng dụng sẽ loại bỏ sự nhầm lẫn của việc sử dụng regex. Tôi không thể nhận xét về giá trị "bí mật" của nó mà không có ngữ cảnh.
PythonTester

Tôi sẽ cẩn thận sử dụng literal_eval mà mong đợi chuỗi python sau = hoặc: do đó bạn không thể sử dụng được nữa ví dụ path1 = / some / path / nhưng path1 = '/ some / path /'
vldbnc

21

Không đề cập đến converterskwarg choConfigParser() trong bất kỳ câu trả lời nào là khá thất vọng.

Theo tài liệu bạn có thể chuyển từ điển ConfigParser đó sẽ thêm một getphương thức cho cả trình phân tích cú pháp và phần proxy. Vì vậy, cho một danh sách:

example.ini

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

Ví dụ phân tích cú pháp:

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']

Đây là mục yêu thích cá nhân của tôi vì không cần phân lớp phụ và tôi không phải dựa vào người dùng cuối để viết JSON hoàn hảo hoặc danh sách có thể diễn giải theo ast.literal_eval.


15

Tôi đáp xuống đây để tìm cách tiêu thụ ...

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

Câu trả lời là phân tách nó trên dấu phẩy và tước khoảng trắng:

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

Để có kết quả danh sách:

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

Nó có thể không trả lời chính xác câu hỏi của OP nhưng có thể là câu trả lời đơn giản mà một số người đang tìm kiếm.


2
Tôi nghĩ Dick đã ở sorger@espionage.su! Không có gì ngạc nhiên khi thư của tôi tiếp tục bị trả lại! > _ <
Augusta

1
Đọc bình luận này 4 năm sau và cười thầm với quả trứng Phục sinh
một kỹ sư tò mò

11

Đây là những gì tôi sử dụng cho danh sách:

nội dung tập tin cấu hình:

[sect]
alist = a
        b
        c

mã:

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

nó hoạt động cho chuỗi

trong trường hợp số

cấu hình nội dung:

nlist = 1
        2
        3

mã:

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

cảm ơn.


Đây là cái mà tôi thực sự đang tìm kiếm cảm ơn @LittleEaster
ashley

5

Vì vậy, một cách khác, mà tôi thích, là chỉ phân chia các giá trị, ví dụ:

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

Có thể được tải như thế này vào một danh sách các chuỗi hoặc số nguyên, như sau:

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(',')]

Phương pháp này ngăn bạn không cần phải bọc các giá trị của mình trong ngoặc để tải dưới dạng JSON.


Xin chào Mitch, trong trường hợp sau, sẽ không tốt hơn khi sử dụng get_int ('first_row'). Split (',') thay vì chuyển đổi rõ ràng thành int trong khi lặp?
Guido

2

Chỉ các kiểu nguyên thủy được hỗ trợ để tuần tự hóa bởi trình phân tích cú pháp cấu hình. Tôi sẽ sử dụng JSON hoặc YAML cho loại yêu cầu đó.


cảm ơn đã làm rõ, utku. vấn đề duy nhất là tôi không thể sử dụng các gói bên ngoài vào lúc này. tôi nghĩ rằng tôi sẽ viết một lớp đơn giản để xử lý việc này. Tôi sẽ chia sẻ nó cuối cùng.
pistacchio

Phiên bản Python nào bạn đang chạy? Mô-đun JSON được bao gồm với 2.6.
Patrick Harrington

2

Tôi đã đối mặt với cùng một vấn đề trong quá khứ. Nếu bạn cần các danh sách phức tạp hơn, hãy xem xét việc tạo trình phân tích cú pháp của riêng bạn bằng cách kế thừa từ ConfigParser. Sau đó, bạn sẽ ghi đè lên phương thức get với điều đó:

    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

Với giải pháp này, bạn cũng sẽ có thể xác định từ điển trong tệp cấu hình của mình.

Nhưng hãy cẩn thận! Điều này không an toàn: điều này có nghĩa là bất kỳ ai cũng có thể chạy mã thông qua tệp cấu hình của bạn. Nếu bảo mật không phải là vấn đề trong dự án của bạn, tôi sẽ xem xét sử dụng các lớp python trực tiếp làm tệp cấu hình. Phần sau mạnh hơn và có thể sử dụng nhiều hơn tệp ConfigParser:

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

Tuy nhiên, tôi đã nghĩ đến việc làm điều này: tại sao không có các giá trị cấu hình được thiết lập như thế barList=item1,item2và sau đó gọi if value.find(',') > 0: return value.split(','), hoặc tốt hơn là, ứng dụng phân tích tất cả các tùy chọn cấu hình dưới dạng danh sách và chỉ .split(',')mọi thứ mù quáng?
Dropogans

1
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(',')

Vì vậy, bây giờ config.cfgtập tin của tôi , có thể trông như thế này:

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

Có thể được phân tích cú pháp thành các đối tượng đủ chi tiết cho dự án nhỏ của tôi.

>>> 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'

Điều này là để phân tích cú pháp các cấu hình đơn giản rất nhanh, bạn mất tất cả khả năng tìm nạp int, bools và các loại đầu ra khác mà không chuyển đổi đối tượng được trả về Parserhoặc thực hiện lại công việc phân tích cú pháp được thực hiện bởi lớp Parser ở nơi khác.


1

Tôi đã hoàn thành nhiệm vụ tương tự trong dự án của mình với phần có khóa không có giá trị:

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)

Đầu ra:

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

ứng dụng.config:

[FEATURES]
BIOtag
TextPosition
IsNoun
IsNomn

0

json.loads & ast.literal_eval dường như đang hoạt động nhưng danh sách đơn giản trong cấu hình đang coi mỗi ký tự là byte nên trả về dấu ngoặc vuông ....

nghĩa là nếu cấu hình có fieldvalue = [1,2,3,4,5]

sau đó config.read(*.cfg) config['fieldValue'][0]trở về [vị trí của1


0

Như Peter Smit đã đề cập ( https://stackoverflow.com/a/11866695/7424596 ) Bạn có thể muốn mở rộng ConfigParser, ngoài ra, Bộ nội suy có thể được sử dụng để tự động chuyển đổi vào và từ danh sách.

Để tham khảo ở phía dưới, bạn có thể tìm thấy mã tự động chuyển đổi cấu hình như:

[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
  ]

Vì vậy, nếu bạn yêu cầu khóa, bạn sẽ nhận được:

<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']

Mã số:

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 ghi nhớ tầm quan trọng của sự thụt vào. Khi đọc trong chuỗi tài liệu ConfigParser:

Các giá trị có thể trải dài trên nhiều dòng, miễn là chúng được thụt sâu hơn dòng đầu tiên của giá trị. Tùy thuộc vào chế độ của trình phân tích cú pháp, các dòng trống có thể được coi là một phần của các giá trị đa dòng hoặc bị bỏ qua.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.