중첩 된 사전을 예쁘게 인쇄하는 방법?
파이썬에서 깊이가 ~ 4 인 사전을 어떻게 인쇄합니까? 로 예쁜 인쇄를 시도했지만 pprint()작동하지 않았습니다.
import pprint
pp = pprint.PrettyPrinter(indent=4)
pp.pprint(mydict)
"\t"각 중첩마다 들여 쓰기 ( )를 원하므로 다음과 같이 얻을 수 있습니다.
key1
value1
value2
key2
value1
value2
기타
어떻게해야합니까?
서식이 정확히 어떻게 표시되는지 잘 모르겠지만 다음과 같은 기능으로 시작할 수 있습니다.
def pretty(d, indent=0):
for key, value in d.items():
print('\t' * indent + str(key))
if isinstance(value, dict):
pretty(value, indent+1)
else:
print('\t' * (indent+1) + str(value))
내 첫 번째 생각은 JSON 시리얼 라이저가 아마도 중첩 된 사전에 능숙하다고 생각했기 때문에 속임수를 사용합니다.
>>> import json
>>> print json.dumps({'a':2, 'b':{'x':3, 'y':{'t1': 4, 't2':5}}},
... sort_keys=True, indent=4)
{
"a": 2,
"b": {
"x": 3,
"y": {
"t1": 4,
"t2": 5
}
}
}
PyYAML을 통해 YAML 을 시도 할 수 있습니다. 출력을 미세 조정할 수 있습니다. 다음으로 시작하는 것이 좋습니다.
print yaml.dump(data, allow_unicode=True, default_flow_style=False)
결과는 매우 읽기 쉽습니다. 필요한 경우 파이썬으로 다시 파싱 할 수도 있습니다.
편집하다:
예:
>>> import yaml
>>> data = {'a':2, 'b':{'x':3, 'y':{'t1': 4, 't2':5}}}
>>> print yaml.dump(data, default_flow_style=False)
a: 2
b:
x: 3
y:
t1: 4
t2: 5
수행 한 결과, 최소한 간단한 형식으로 파이썬 인터프리터의 출력을 모방 한 프린터가 보이지 않습니다.
class Formatter(object):
def __init__(self):
self.types = {}
self.htchar = '\t'
self.lfchar = '\n'
self.indent = 0
self.set_formater(object, self.__class__.format_object)
self.set_formater(dict, self.__class__.format_dict)
self.set_formater(list, self.__class__.format_list)
self.set_formater(tuple, self.__class__.format_tuple)
def set_formater(self, obj, callback):
self.types[obj] = callback
def __call__(self, value, **args):
for key in args:
setattr(self, key, args[key])
formater = self.types[type(value) if type(value) in self.types else object]
return formater(self, value, self.indent)
def format_object(self, value, indent):
return repr(value)
def format_dict(self, value, indent):
items = [
self.lfchar + self.htchar * (indent + 1) + repr(key) + ': ' +
(self.types[type(value[key]) if type(value[key]) in self.types else object])(self, value[key], indent + 1)
for key in value
]
return '{%s}' % (','.join(items) + self.lfchar + self.htchar * indent)
def format_list(self, value, indent):
items = [
self.lfchar + self.htchar * (indent + 1) + (self.types[type(item) if type(item) in self.types else object])(self, item, indent + 1)
for item in value
]
return '[%s]' % (','.join(items) + self.lfchar + self.htchar * indent)
def format_tuple(self, value, indent):
items = [
self.lfchar + self.htchar * (indent + 1) + (self.types[type(item) if type(item) in self.types else object])(self, item, indent + 1)
for item in value
]
return '(%s)' % (','.join(items) + self.lfchar + self.htchar * indent)
그것을 초기화하려면 :
pretty = Formatter()
정의 된 형식에 대한 포맷터 추가를 지원할 수 있습니다. 간단히 이와 같은 기능을 만들고 set_formater로 원하는 형식에 바인딩하면됩니다.
from collections import OrderedDict
def format_ordereddict(self, value, indent):
items = [
self.lfchar + self.htchar * (indent + 1) +
"(" + repr(key) + ', ' + (self.types[
type(value[key]) if type(value[key]) in self.types else object
])(self, value[key], indent + 1) + ")"
for key in value
]
return 'OrderedDict([%s])' % (','.join(items) +
self.lfchar + self.htchar * indent)
pretty.set_formater(OrderedDict, format_ordereddict)
역사적 이유로, 클래스 대신 함수였던 이전의 예쁜 프린터를 유지하지만 둘 다 같은 방식으로 사용할 수 있습니다. 클래스 버전은 단순히 더 많은 것을 허용합니다.
def pretty(value, htchar='\t', lfchar='\n', indent=0):
nlch = lfchar + htchar * (indent + 1)
if type(value) is dict:
items = [
nlch + repr(key) + ': ' + pretty(value[key], htchar, lfchar, indent + 1)
for key in value
]
return '{%s}' % (','.join(items) + lfchar + htchar * indent)
elif type(value) is list:
items = [
nlch + pretty(item, htchar, lfchar, indent + 1)
for item in value
]
return '[%s]' % (','.join(items) + lfchar + htchar * indent)
elif type(value) is tuple:
items = [
nlch + pretty(item, htchar, lfchar, indent + 1)
for item in value
]
return '(%s)' % (','.join(items) + lfchar + htchar * indent)
else:
return repr(value)
그것을 사용하려면 :
>>> a = {'list':['a','b',1,2],'dict':{'a':1,2:'b'},'tuple':('a','b',1,2),'function':pretty,'unicode':u'\xa7',("tuple","key"):"valid"}
>>> a
{'function': <function pretty at 0x7fdf555809b0>, 'tuple': ('a', 'b', 1, 2), 'list': ['a', 'b', 1, 2], 'dict': {'a': 1, 2: 'b'}, 'unicode': u'\xa7', ('tuple', 'key'): 'valid'}
>>> print(pretty(a))
{
'function': <function pretty at 0x7fdf555809b0>,
'tuple': (
'a',
'b',
1,
2
),
'list': [
'a',
'b',
1,
2
],
'dict': {
'a': 1,
2: 'b'
},
'unicode': u'\xa7',
('tuple', 'key'): 'valid'
}
다른 버전과 비교 :
- 이 솔루션은 객체 유형을 직접 검색하므로 목록이나 받아쓰기뿐만 아니라 거의 모든 것을 인쇄 할 수 있습니다.
- 의존성이 없습니다.
- 모든 것이 문자열 안에 들어가므로 원하는대로 무엇이든 할 수 있습니다.
- 클래스와 함수는 테스트되었으며 Python 2.7 및 3.4에서 작동합니다.
- 모든 유형의 객체를 내부에 가질 수 있습니다. 결과에 넣는 내용이 아니라 표현입니다 (문자열에는 따옴표가 있고 유니 코드 문자열은 완전히 표시됩니다 ...).
- 클래스 버전을 사용하면 원하는 모든 객체 유형에 대한 서식을 추가하거나 이미 정의 된 객체 유형에 대해 형식을 변경할 수 있습니다.
- key는 유효한 유형일 수 있습니다.
- 들여 쓰기 및 줄 바꿈 문자는 원하는 모든 항목으로 변경할 수 있습니다.
- Dict, List 및 Tuples는 꽤 인쇄됩니다.
이 방법으로 당신은 예를 들어 사전 이름이 yasin 예쁘게 인쇄 할 수 있습니다
import json
print (json.dumps(yasin, indent=2))
다른 옵션 yapf:
from pprint import pformat
from yapf.yapflib.yapf_api import FormatCode
dict_example = {'1': '1', '2': '2', '3': [1, 2, 3, 4, 5], '4': {'1': '1', '2': '2', '3': [1, 2, 3, 4, 5]}}
dict_string = pformat(dict_example)
formatted_code, _ = FormatCode(dict_string)
print(formatted_code)
산출:
{
'1': '1',
'2': '2',
'3': [1, 2, 3, 4, 5],
'4': {
'1': '1',
'2': '2',
'3': [1, 2, 3, 4, 5]
}
}
다른 사람들이 게시 한 것처럼 recursion / dfs를 사용하여 중첩 된 사전 데이터를 인쇄하고 사전 인 경우 재귀 적으로 호출 할 수 있습니다. 그렇지 않으면 데이터를 인쇄하십시오.
def print_json(data):
if type(data) == dict:
for k, v in data.items():
print k
print_json(v)
else:
print data
나는 sth의 대답을 가져 와서 중첩 된 사전과 목록의 요구에 맞게 약간 수정했습니다.
def pretty(d, indent=0):
if isinstance(d, dict):
for key, value in d.iteritems():
print '\t' * indent + str(key)
if isinstance(value, dict) or isinstance(value, list):
pretty(value, indent+1)
else:
print '\t' * (indent+1) + str(value)
elif isinstance(d, list):
for item in d:
if isinstance(item, dict) or isinstance(item, list):
pretty(item, indent+1)
else:
print '\t' * (indent+1) + str(item)
else:
pass
그러면 다음과 같은 출력이 나타납니다.
>>>
xs:schema
@xmlns:xs
http://www.w3.org/2001/XMLSchema
xs:redefine
@schemaLocation
base.xsd
xs:complexType
@name
Extension
xs:complexContent
xs:restriction
@base
Extension
xs:sequence
xs:element
@name
Policy
@minOccurs
1
xs:complexType
xs:sequence
xs:element
...
pout 은 예를 들어 ( data다른 답변에서 차용) 던지는 것을 인쇄 할 수 있습니다 .
data = {'a':2, 'b':{'x':3, 'y':{'t1': 4, 't2':5}}}
pout.vs(data)
다음과 같이 화면에 출력이 인쇄됩니다.
{
'a': 2,
'b':
{
'y':
{
't2': 5,
't1': 4
},
'x': 3
}
}
또는 객체의 형식화 된 문자열 출력을 반환 할 수 있습니다.
v = pout.s(data)
그것의 주요 사용 사례는 디버깅을위한 것이므로 객체 인스턴스 또는 다른 것들을 질식시키지 않으며 예상대로 유니 코드 출력을 처리합니다. 파이썬 2.7 및 3에서 작동합니다.
공개 : 저는 pout의 저자이자 관리자입니다.
Sth, 나는 꽤 예쁘다.)
def pretty(d, indent=0):
for key, value in d.iteritems():
if isinstance(value, dict):
print '\t' * indent + (("%30s: {\n") % str(key).upper())
pretty(value, indent+1)
print '\t' * indent + ' ' * 32 + ('} # end of %s #\n' % str(key).upper())
elif isinstance(value, list):
for val in value:
print '\t' * indent + (("%30s: [\n") % str(key).upper())
pretty(val, indent+1)
print '\t' * indent + ' ' * 32 + ('] # end of %s #\n' % str(key).upper())
else:
print '\t' * indent + (("%30s: %s") % (str(key).upper(),str(value)))
This class prints out a complex nested dictionary with sub dictionaries and sub lists.
##
## Recursive class to parse and print complex nested dictionary
##
class NestedDictionary(object):
def __init__(self,value):
self.value=value
def print(self,depth):
spacer="--------------------"
if type(self.value)==type(dict()):
for kk, vv in self.value.items():
if (type(vv)==type(dict())):
print(spacer[:depth],kk)
vvv=(NestedDictionary(vv))
depth=depth+3
vvv.print(depth)
depth=depth-3
else:
if (type(vv)==type(list())):
for i in vv:
vvv=(NestedDictionary(i))
depth=depth+3
vvv.print(depth)
depth=depth-3
else:
print(spacer[:depth],kk,vv)
##
## Instatiate and execute - this prints complex nested dictionaries
## with sub dictionaries and sub lists
## 'something' is a complex nested dictionary
MyNest=NestedDictionary(weather_com_result)
MyNest.print(0)
이 간단한 코드를 작성하여 파이썬에서 json 객체의 일반적인 구조를 인쇄했습니다.
def getstructure(data, tab = 0):
if type(data) is dict:
print ' '*tab + '{'
for key in data:
print ' '*tab + ' ' + key + ':'
getstructure(data[key], tab+4)
print ' '*tab + '}'
elif type(data) is list and len(data) > 0:
print ' '*tab + '['
getstructure(data[0], tab+4)
print ' '*tab + ' ...'
print ' '*tab + ']'
다음 데이터의 결과
a = {'list':['a','b',1,2],'dict':{'a':1,2:'b'},'tuple':('a','b',1,2),'function':'p','unicode':u'\xa7',("tuple","key"):"valid"}
getstructure(a)
매우 컴팩트하고 다음과 같습니다.
{
function:
tuple:
list:
[
...
]
dict:
{
a:
2:
}
unicode:
('tuple', 'key'):
}
다음은 모든 종류의 중첩 된 사전을 인쇄하고 그 과정에서 "부모"사전을 추적하는 내용입니다.
dicList = list()
def prettierPrint(dic, dicList):
count = 0
for key, value in dic.iteritems():
count+=1
if str(value) == 'OrderedDict()':
value = None
if not isinstance(value, dict):
print str(key) + ": " + str(value)
print str(key) + ' was found in the following path:',
print dicList
print '\n'
elif isinstance(value, dict):
dicList.append(key)
prettierPrint(value, dicList)
if dicList:
if count == len(dic):
dicList.pop()
count = 0
prettierPrint(dicExample, dicList)
이것은 OP에 지정된 것과 같은 다른 형식에 따라 인쇄하기에 좋은 출발점 입니다. 인쇄 블록 주변의 작업 만하면됩니다. 값이 'OrderedDict ()'인지 확인합니다. Container datatypes Collections 에서 무언가를 사용하고 있는지에 따라 elif 블록이 이름으로 인해 추가 사전으로 보지 않도록 이러한 종류의 페일 세이프를 만들어야합니다 . 현재로서는 사전 예를 들어
example_dict = {'key1': 'value1',
'key2': 'value2',
'key3': {'key3a': 'value3a'},
'key4': {'key4a': {'key4aa': 'value4aa',
'key4ab': 'value4ab',
'key4ac': 'value4ac'},
'key4b': 'value4b'}
인쇄합니다
key3a: value3a
key3a was found in the following path: ['key3']
key2: value2
key2 was found in the following path: []
key1: value1
key1 was found in the following path: []
key4ab: value4ab
key4ab was found in the following path: ['key4', 'key4a']
key4ac: value4ac
key4ac was found in the following path: ['key4', 'key4a']
key4aa: value4aa
key4aa was found in the following path: ['key4', 'key4a']
key4b: value4b
key4b was found in the following path: ['key4']
~ 질문 형식에 맞는 코드 변경 ~
lastDict = list()
dicList = list()
def prettierPrint(dic, dicList):
global lastDict
count = 0
for key, value in dic.iteritems():
count+=1
if str(value) == 'OrderedDict()':
value = None
if not isinstance(value, dict):
if lastDict == dicList:
sameParents = True
else:
sameParents = False
if dicList and sameParents is not True:
spacing = ' ' * len(str(dicList))
print dicList
print spacing,
print str(value)
if dicList and sameParents is True:
print spacing,
print str(value)
lastDict = list(dicList)
elif isinstance(value, dict):
dicList.append(key)
prettierPrint(value, dicList)
if dicList:
if count == len(dic):
dicList.pop()
count = 0
동일한 예제 코드를 사용하여 다음을 인쇄합니다.
['key3']
value3a
['key4', 'key4a']
value4ab
value4ac
value4aa
['key4']
value4b
이것은 아닙니다 정확히 OP에 요청 것을. 차이점은 부모가없는 경우 공백으로 대체되지 않고 여전히 인쇄됩니다. OP의 형식에 도착하려면 다음과 같은 작업을 수행해야합니다 : 반복적으로 비교 dicList을 와 lastDict . 다음과 같은 경우 점검, 새 사전을 만들고 그것에 dicList의 내용을 복사하여이 작업을 수행 할 수 있습니다 내가 복사 사전에이와 동일 내가 lastDict, 그리고 -이 경우 - 그에 공백을 쓰고 난 문자열 멀티 플라이어 기능을 사용하여 위치 .
에서 이 링크 :
def prnDict(aDict, br='\n', html=0,
keyAlign='l', sortKey=0,
keyPrefix='', keySuffix='',
valuePrefix='', valueSuffix='',
leftMargin=0, indent=1 ):
'''
return a string representive of aDict in the following format:
{
key1: value1,
key2: value2,
...
}
Spaces will be added to the keys to make them have same width.
sortKey: set to 1 if want keys sorted;
keyAlign: either 'l' or 'r', for left, right align, respectively.
keyPrefix, keySuffix, valuePrefix, valueSuffix: The prefix and
suffix to wrap the keys or values. Good for formatting them
for html document(for example, keyPrefix='<b>', keySuffix='</b>').
Note: The keys will be padded with spaces to have them
equally-wide. The pre- and suffix will be added OUTSIDE
the entire width.
html: if set to 1, all spaces will be replaced with ' ', and
the entire output will be wrapped with '<code>' and '</code>'.
br: determine the carriage return. If html, it is suggested to set
br to '<br>'. If you want the html source code eazy to read,
set br to '<br>\n'
version: 04b52
author : Runsun Pan
require: odict() # an ordered dict, if you want the keys sorted.
Dave Benjamin
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/161403
'''
if aDict:
#------------------------------ sort key
if sortKey:
dic = aDict.copy()
keys = dic.keys()
keys.sort()
aDict = odict()
for k in keys:
aDict[k] = dic[k]
#------------------- wrap keys with ' ' (quotes) if str
tmp = ['{']
ks = [type(x)==str and "'%s'"%x or x for x in aDict.keys()]
#------------------- wrap values with ' ' (quotes) if str
vs = [type(x)==str and "'%s'"%x or x for x in aDict.values()]
maxKeyLen = max([len(str(x)) for x in ks])
for i in range(len(ks)):
#-------------------------- Adjust key width
k = {1 : str(ks[i]).ljust(maxKeyLen),
keyAlign=='r': str(ks[i]).rjust(maxKeyLen) }[1]
v = vs[i]
tmp.append(' '* indent+ '%s%s%s:%s%s%s,' %(
keyPrefix, k, keySuffix,
valuePrefix,v,valueSuffix))
tmp[-1] = tmp[-1][:-1] # remove the ',' in the last item
tmp.append('}')
if leftMargin:
tmp = [ ' '*leftMargin + x for x in tmp ]
if html:
return '<code>%s</code>' %br.join(tmp).replace(' ',' ')
else:
return br.join(tmp)
else:
return '{}'
'''
Example:
>>> a={'C': 2, 'B': 1, 'E': 4, (3, 5): 0}
>>> print prnDict(a)
{
'C' :2,
'B' :1,
'E' :4,
(3, 5):0
}
>>> print prnDict(a, sortKey=1)
{
'B' :1,
'C' :2,
'E' :4,
(3, 5):0
}
>>> print prnDict(a, keyPrefix="<b>", keySuffix="</b>")
{
<b>'C' </b>:2,
<b>'B' </b>:1,
<b>'E' </b>:4,
<b>(3, 5)</b>:0
}
>>> print prnDict(a, html=1)
<code>{
'C' :2,
'B' :1,
'E' :4,
(3, 5):0
}</code>
>>> b={'car': [6, 6, 12], 'about': [15, 9, 6], 'bookKeeper': [9, 9, 15]}
>>> print prnDict(b, sortKey=1)
{
'about' :[15, 9, 6],
'bookKeeper':[9, 9, 15],
'car' :[6, 6, 12]
}
>>> print prnDict(b, keyAlign="r")
{
'car':[6, 6, 12],
'about':[15, 9, 6],
'bookKeeper':[9, 9, 15]
}
'''
나는 sth 의 대답 을 취하고 작지만 매우 유용한 수정을 한 후에이 질문으로 돌아갑니다 . 이 기능은 모든 인쇄 json으로 트리 키 뿐만 아니라 잎 노드의 크기가 그 나무에서입니다.
def print_JSON_tree(d, indent=0):
for key, value in d.iteritems():
print ' ' * indent + unicode(key),
if isinstance(value, dict):
print; print_JSON_tree(value, indent+1)
else:
print ":", str(type(d[key])).split("'")[1], "-", str(len(unicode(d[key])))
큰 JSON 객체가 있고 고기가 어디에 있는지 알아 내고 싶을 때 정말 좋습니다. 예 :
>>> print_JSON_tree(JSON_object)
key1
value1 : int - 5
value2 : str - 16
key2
value1 : str - 34
value2 : list - 5623456
이것은 JSON_object['key1']['key2']['value2']문자열로 포맷 된 값의 길이가 매우 크기 때문에 관심있는 대부분의 데이터가 아마도 내부 에 있음을 알려줍니다.
이 기능을 사용하십시오 :
def pretty_dict(d, n=1):
for k in d:
print(" "*n + k)
try:
pretty_dict(d[k], n=n+4)
except TypeError:
continue
다음과 같이 호출하십시오.
pretty_dict(mydict)
나는 상대적으로 파이썬 초보자이지만 지난 몇 주 동안 중첩 된 사전을 사용하여 왔으며 이것이 내가 생각해 낸 것입니다.
스택을 사용해보십시오. 루트 사전의 키를 목록 목록으로 만드십시오.
stack = [ root.keys() ] # Result: [ [root keys] ]
마지막 순서부터 역순으로, 사전에서 각 키를 찾아서 값이 사전인지 확인합니다. 그렇지 않은 경우 키를 인쇄 한 후 삭제하십시오. 키 값은 그러나 경우 이다 사전은, 각 키의 새로운리스트에 대해 재귀 적으로 반복 키가 그 스택의 단부에 그 값에 대한 키를 추가 인쇄와 같은 방법으로 그리스트를 처리하기 시작한다.
각 목록의 두 번째 키 값이 사전이라면 몇 라운드 후에 다음과 같이 표시됩니다.
[['key 1','key 2'],['key 2.1','key 2.2'],['key 2.2.1','key 2.2.2'],[`etc.`]]
이 접근법의 단점은 들여 쓰기가 \t스택 길이의 몇 배에 불과하다는 것입니다 .
indent = "\t" * len(stack)
단점은 각 키를 확인하기 위해 관련 하위 사전으로 해시해야하지만 목록 이해와 간단한 for루프로 쉽게 처리 할 수 있다는 것입니다 .
path = [li[-1] for li in stack]
# The last key of every list of keys in the stack
sub = root
for p in path:
sub = sub[p]
if type(sub) == dict:
stack.append(sub.keys()) # And so on
이 방법을 사용하려면 후행 빈 목록을 정리 하고 목록 에서 마지막 키를 삭제 한 다음 빈 목록 을 삭제해야합니다 (물론 다른 빈 목록 등을 생성 할 수 있음).
이 방법을 구현할 수있는 다른 방법이 있지만이 방법을 사용하는 방법에 대한 기본 아이디어를 제공하기를 바랍니다.
편집 : 당신이 모든 것을 거치고 싶지 않다면, pprint모듈은 좋은 형식으로 중첩 된 사전을 인쇄합니다.
여기에 sth의 의견에 따라 작성한 함수가 있습니다. 들여 쓰기가있는 json.dumps와 동일하게 작동하지만 들여 쓰기 공간 대신 탭을 사용하고 있습니다. Python 3.2 이상에서는 들여 쓰기를 '\ t'로 지정할 수 있지만 2.7에서는 지정할 수 없습니다.
def pretty_dict(d):
def pretty(d, indent):
for i, (key, value) in enumerate(d.iteritems()):
if isinstance(value, dict):
print '{0}"{1}": {{'.format( '\t' * indent, str(key))
pretty(value, indent+1)
if i == len(d)-1:
print '{0}}}'.format( '\t' * indent)
else:
print '{0}}},'.format( '\t' * indent)
else:
if i == len(d)-1:
print '{0}"{1}": "{2}"'.format( '\t' * indent, str(key), value)
else:
print '{0}"{1}": "{2}",'.format( '\t' * indent, str(key), value)
print '{'
pretty(d,indent=1)
print '}'
전의:
>>> dict_var = {'a':2, 'b':{'x':3, 'y':{'t1': 4, 't2':5}}}
>>> pretty_dict(dict_var)
{
"a": "2",
"b": {
"y": {
"t2": "5",
"t1": "4"
},
"x": "3"
}
}
참고 URL : https://stackoverflow.com/questions/3229419/how-to-pretty-print-nested-dictionaries
'development' 카테고리의 다른 글
| JAX-RS — JSON과 HTTP 상태 코드를 함께 반환하는 방법? (0) | 2020.04.12 |
|---|---|
| 함수에서 jQuery 대화 상자의 버튼을 비활성화하려면 어떻게합니까? (0) | 2020.04.12 |
| 일요일마다 매주 crontab 작업을 실행하는 방법 (0) | 2020.04.12 |
| JavaScript 또는 jQuery를 사용하여 이번 달의 첫 번째 및 마지막 날짜 가져 오기 (0) | 2020.04.12 |
| ImageView 소스 변경 (0) | 2020.04.12 |