รายการใน ConfigParser


182

โดยทั่วไป ConfigParser สร้างรูปลักษณ์ไฟล์เช่น:

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

ตอนนี้มีวิธีการจัดทำดัชนีรายการเช่น:

[Section 3]
barList={
    item1,
    item2
}

คำถามที่เกี่ยวข้อง: คีย์ที่ไม่ซ้ำกันของ ConfigParser Python ต่อส่วน

คำตอบ:


142

ไม่มีอะไรหยุดคุณจากการบรรจุรายการลงในสตริงที่มีการคั่นแล้วคลายออกเมื่อคุณได้รับสตริงจากการกำหนดค่า หากคุณทำเช่นนี้ในส่วนกำหนดค่าของคุณจะมีลักษณะดังนี้:

[Section 3]
barList=item1,item2

มันไม่สวย แต่ใช้งานได้กับรายการที่ง่ายที่สุด


2
และถ้าคุณมีรายการที่ซับซ้อนคุณสามารถอ้างถึงคำถามนี้: stackoverflow.com/questions/330900/… :-)
John Fouhy

วิธีแก้ปัญหาที่ดี แต่จะทำอย่างไรถ้าไม่มีตัวคั่นที่เป็นไปได้เกินกว่าที่คุณจะรับประกันได้ว่าจะไม่ปรากฏในรายการ
Wim

@wim ดูคำตอบของฉันคุณสามารถใช้ \ n เป็นตัวคั่น
Peter Smit

@wim คุณจะต้องใช้วิธีการหนีตัวละครตัวคั่นถ้ามันสามารถเป็นตัวละครที่ถูกต้องตามกฎหมาย (และวิธีที่จะหลบหนีตัวละครที่คุณใช้ในการหลบหนี)
jamesdlin

ถ้ารายการมีองค์ประกอบเดียว
Sérgio Mafra

223

สายไปหน่อย แต่อาจมีประโยชน์สำหรับบางคน ฉันใช้การรวมกันของ 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] มีประโยชน์มาก


1
มันยอดเยี่ยมเพราะมันจะทำการ "ส่ง" ค่าโดยอัตโนมัติซึ่งอาจมีประโยชน์หากคุณไม่ทราบประเภทล่วงหน้า
LeGBT

ฉันชอบความคิดนี้ แต่ฉันสามารถทำให้มันใช้ได้กับรายการตัวเลขเท่านั้น เครื่องหมายคำพูดไม่ได้ช่วย แปลก. กำลังเดินทางไป.
rsaw

5
คุณจะต้องมี [ "A", "B", "C"] สำหรับสตริงสำหรับพวกเขาในการทำงาน สำหรับฉันการคลิกเพื่อดูตัวเลข แต่เนื่องจากไฟล์ cfg นั้นสามารถแก้ไขได้โดยส่วนใหญ่การเพิ่มทุกครั้งจะเป็นเรื่องลำบาก ฉันควรใช้เครื่องหมายจุลภาคแล้วแบ่งมัน
Saurabh Hirani

ทางออกที่สวยงามโดยใช้เพียงห้องสมุดมาตรฐาน ยินดีที่ได้ใช้ความคิดเห็นและ json
wi1

มันจะทำงานอย่างไรสำหรับสตริงดิบเช่นkey5 : [r"abc $x_i$", r"def $y_j$"]? พวกเขายกข้อผิดพลาดjson.decoder.JSONDecodeError: Expecting value: line 1 column 2 (char 1)
kingusiu

101

มางานปาร์ตี้สายนี้ แต่ฉันเพิ่งนำสิ่งนี้ไปใช้กับส่วนเฉพาะในไฟล์ปรับแต่งสำหรับรายการ:

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

และการใช้config.items( "paths" )เพื่อรับรายการ iterable ของรายการเส้นทางเช่น:

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

หวังว่าจะช่วยให้ชาวบ้านคนอื่น ๆ Googling คำถามนี้;)


3
ผมชอบวิธีนี้เพราะคุณสามารถ; commentรายการออกบางอย่างจากรายการโดยไม่ต้องเขียนรายชื่อทั้งหมด
Wim

1
+1 แต่ถ้าคุณทำเช่นนี้ให้ระวังด้วยการใช้keyเนื่องจาก ConfigParser แปลงคีย์ดังกล่าวทั้งหมดเป็นตัวพิมพ์
เล็ก

4
@AlexDean คุณสามารถตั้งค่า ConfigParser เพื่อปล่อย camelCase ให้เข้าที่โดยการตั้งค่า optionxform = str ตัวอย่าง: config = ConfigParser.SafeConfigParser() config.optionxform = str แล้วกรณีที่จะถูกทิ้งไว้ตามลำพัง
คาเมรอนกูเดล

@Henry Cooke คุณได้ทำการทดสอบแล้วว่าเมื่อใดที่มีการระบุรหัสไว้หลายครั้ง
DevPlayer

1
@DevPlayer ด้วยการใช้งานหลายคีย์คุณจะได้รับค่าสุดท้ายเท่านั้น (ตอบกลับความคิดเห็นเก่า ๆ 2 ปีเพื่อประโยชน์ของผู้อ่านคนอื่น ๆ )
Marcin K

63

สิ่งหนึ่งที่ผู้คนจำนวนมากไม่รู้คืออนุญาตให้กำหนดค่าหลายค่าได้ ตัวอย่างเช่น:

;test.ini
[hello]
barlist = 
    item1
    item2

มูลค่าของconfig.get('hello','barlist')ตอนนี้จะเป็น:

"\nitem1\nitem2"

ซึ่งคุณสามารถแยกด้วยวิธีแยกได้อย่างง่ายดาย (อย่าลืมกรองรายการที่ว่างเปล่า)

ถ้าเรามองไปที่กรอบใหญ่เช่นพีระมิดพวกเขาจะใช้เทคนิคนี้:

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. บรรทัดต่อไปนี้ทั้งหมดที่ขึ้นต้นด้วยช่องว่างถือว่าเป็นส่วนหนึ่งของรายการก่อนหน้า นอกจากนี้ถ้ามันมี = สัญญาณหรือถ้ามันเริ่มต้นด้วย; ต่อไปนี้ช่องว่าง

ทำไมคุณถึงใช้.splitlines()แทน.split()? เมื่อใช้พฤติกรรมเริ่มต้นของแต่ละการแบ่งจะดีกว่าอย่างชัดเจน (กรองบรรทัดว่าง) เว้นแต่ฉันหายไปบางสิ่งบางอย่าง ...
rsaw

7
.split () หยุดพักในช่องว่างทั้งหมด (เว้นแต่จะมีการระบุอักขระเฉพาะ), .splitlines () จะแบ่งตามอักขระบรรทัดใหม่ทั้งหมด
Peter Smit

จุดที่ดี Ahhh ฉันไม่ได้คิดเกี่ยวกับเรื่องนี้เพราะไม่มีค่าของฉันมีช่องว่าง
rsaw

38

หากคุณต้องการส่งผ่านรายการอย่างแท้จริงคุณสามารถใช้:

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

ในกรณีนี้อะไรคือข้อดีของการใช้ast.literal_eval()เมื่อเปรียบเทียบกับการใช้ (นิยมมากขึ้น) json.loads()? ฉันคิดว่าอันหลังให้ความปลอดภัยมากกว่าใช่ไหม
RayLuo

2
ฉันชอบที่จะเห็นและเป็นตัวอย่างของเรื่องนี้อย่าลังเลที่จะเพิ่มคำตอบในหัวข้อนี้ถ้าคุณรู้สึกว่ามันจะช่วยได้แม้ว่าความคิดเห็นของคุณจะทำให้คำถามที่ดีในตัวเอง คำตอบที่ฉันให้การลดความซับซ้อนของการใช้รายการจาก ConfigParser นั้นเป็นเรื่องภายในของแอพที่ลบการคอมมิทของการใช้ regex ฉันไม่สามารถแสดงความคิดเห็นเกี่ยวกับค่า "ความลับ" ของมันโดยไม่มีบริบท
PythonTester

ฉันจะระมัดระวังการใช้ literal_eval ซึ่งคาดว่าสตริงไพ ธ อนหลังจาก = หรือ: ดังนั้นคุณไม่สามารถใช้อีกต่อไปเช่น path1 = / some / path / แต่ path1 = '/ some / path /'
vldbnc

21

ไม่มีการกล่าวถึงconverterskwarg สำหรับConfigParser()คำตอบใด ๆ เหล่านี้ค่อนข้างน่าผิดหวัง

ตามเอกสารคุณสามารถส่งพจนานุกรมไป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ได้อย่างสมบูรณ์


15

ฉันลงจอดที่นี่เพื่อใช้สิ่งนี้ ...

[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 แน่นอน แต่อาจเป็นคำตอบง่ายๆที่บางคนกำลังมองหา


2
ฉันคิดว่าดิ๊กอยู่ที่sorger@espionage.su! ไม่แปลกใจเลยที่อีเมลของฉันจะเด้ง > _ <
Augusta

1
อ่านความคิดเห็นนี้ 4 ปีต่อมาและหัวเราะเบา ๆ ที่ไข่อีสเตอร์
วิศวกรที่อยากรู้อยากเห็น

11

นี่คือสิ่งที่ฉันใช้สำหรับรายการ:

เนื้อหาไฟล์ปรับแต่ง:

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

ขอบคุณ


นี่คือสิ่งที่ฉันกำลังมองหาขอบคุณจริง ๆ @LittleEaster
ashley

5

อีกวิธีที่ฉันชอบคือการแยกค่าต่างๆเช่น:

#/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


สวัสดีมิทช์ในกรณีหลังจะไม่ดีกว่าที่จะใช้ get_int ('first_row') split (',') แทนที่จะแปลงมันเป็น int อย่างชัดเจนขณะวนลูป
Guido

2

สนับสนุนชนิดดั้งเดิมเท่านั้นสำหรับการทำให้เป็นอนุกรมโดยตัวแยกวิเคราะห์การกำหนดค่า ฉันจะใช้ JSON หรือ YAML สำหรับความต้องการประเภทนั้น


ขอบคุณสำหรับการชี้แจง utku ปัญหาเดียวคือฉันไม่สามารถใช้แพ็คเกจภายนอกในขณะนี้ ฉันคิดว่าฉันจะเขียนคลาสง่ายๆเพื่อจัดการเรื่องนี้ ฉันจะแบ่งปันในที่สุด
pistacchio

คุณใช้ Python เวอร์ชันใด โมดูล JSON นั้นมาพร้อมกับ 2.6
Patrick Harrington

2

ฉันประสบปัญหาเดียวกันในอดีต หากคุณต้องการรายการที่ซับซ้อนมากขึ้นให้พิจารณาสร้าง parser ของคุณเองโดยสืบทอดจาก 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

ด้วยวิธีนี้คุณจะสามารถกำหนดพจนานุกรมในไฟล์ปรับแต่งของคุณได้

แต่ระวังด้วย! สิ่งนี้ไม่ปลอดภัย: หมายความว่าทุกคนสามารถเรียกใช้รหัสผ่านไฟล์ปรับแต่งของคุณได้ หากการรักษาความปลอดภัยไม่ใช่ปัญหาในโครงการของคุณฉันจะพิจารณาใช้คลาสหลามโดยตรงเป็นไฟล์กำหนดค่า ต่อไปนี้มีประสิทธิภาพและใช้งานได้มากกว่าไฟล์ ConfigParser:

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

ฉันกำลังคิดที่จะทำสิ่งนี้ แต่: ทำไมไม่มีการตั้งค่าการกำหนดค่าเช่นbarList=item1,item2แล้วโทรif value.find(',') > 0: return value.split(',')หรือดีกว่ายังมีแอปพลิเคชันแยกตัวเลือกการกำหนดค่าทั้งหมดเป็นรายการและ.split(',')ทุกอย่างเพียงแค่สุ่มสี่สุ่มห้า?
Droogans

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

ดังนั้นตอนนี้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'

นี่คือสำหรับการแยกอย่างรวดเร็วมากของ configs ง่ายคุณสูญเสียความสามารถทั้งหมดที่สามารถดึงข้อมูล ints, bools และประเภทอื่น ๆ ของการส่งออกโดยไม่ต้องเปลี่ยนทั้งวัตถุที่กลับมาจากParserหรือใหม่ทำหน้าที่แยกประสบความสำเร็จโดยชั้น Parser อื่น ๆ


1

ฉันทำงานที่คล้ายกันในโครงการด้วยส่วนที่มีคีย์โดยไม่มีค่า:

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

0

json.loads & ast.literal_evalดูเหมือนว่าจะทำงานได้ แต่รายการที่เรียบง่ายภายใน config กำลังปฏิบัติต่ออักขระแต่ละตัวเป็นไบต์ดังนั้นกลับมาเป็นวงเล็บเหลี่ยม ...

ความหมายถ้า config มี fieldvalue = [1,2,3,4,5]

จากนั้นconfig.read(*.cfg) config['fieldValue'][0]กลับมา[แทนที่1


0

ตามที่ระบุไว้โดย 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)

psเก็บไว้ในใจความสำคัญของการเยื้อง ในฐานะที่อ่านในสตริง Doc ConfigParser:

ค่าสามารถขยายหลายบรรทัดตราบใดที่มีการเยื้องลึกกว่าบรรทัดแรกของค่า ขึ้นอยู่กับโหมดของตัวแยกวิเคราะห์บรรทัดว่างอาจถูกถือว่าเป็นส่วนหนึ่งของค่าหลายบรรทัดหรือถูกละเว้น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.