[英]How to read order of declared members in Python class with use reflection or parsing (metaclass replace is forbidden)?
Important notes to avoid too fast/invalid duplicate tagging - please read it before answer. 重要说明,以避免太快/无效的重复标记-请在回答前阅读它。
Please do not suggest any solution changing original class code - not changing code reflection and parsing is allowed. 请不要提出任何更改原始类代码的解决方案 -不允许更改代码反射和解析。
How to read class attributes in the same order as declared? 如何以与声明相同的顺序读取类属性? is solution - it requires to replace meta class in all classes and add overhead - definitely it is not reflection use. 解决方案-需要替换所有类中的元类并增加开销-绝对不是反射用途。
Consider that I can not or do not want change code to scan class members order . 考虑到我不能或不想更改代码来扫描类成员的命令 。 Classes can have or has unknown metaclasses already - it is not possible to just add metaclass or add performance overhead without reason. 类可以已经具有或具有未知的元类-不可能无缘无故地添加元类或增加性能开销。
Only reflection can be used or parsing files. 只能使用反射或解析文件。
I want to avoid writing parser and read class attributes in order of declaration. 我想避免按声明顺序编写解析器和读取类属性。
How it is possible with use reflection (and simple parsing) in Python? 在Python中使用反射 (和简单解析)怎么可能 ?
Let me give some example: 让我举个例子:
class A(object):
b = 1
a = 1
c = 1
dir(A)
give alphabetic order but required is declaration order. dir(A)
给出字母顺序,但必须是声明顺序。 How to do it - please help? 怎么做-请帮忙?
You'll have to resort to parsing. 您将不得不求助于解析。 You don't need to write a parser here; 您无需在此处编写解析器; the ast
module can do this for you. ast
模块可以为您完成此任务。
Parse the source with ast.parse()
, then walk the resulting tree: 使用ast.parse()
解析源,然后遍历结果树:
class ClassOrder(ast.NodeVisitor):
identifiers = None
def visit_ClassDef(self, node):
self.identifiers = []
for child in node.body:
if isinstance(child, ast.Assign):
for target in child.targets:
self.visit(target)
elif isinstance(child, ast.FunctionDef):
self.identifiers.append(child.name)
def visit_Name(self, node):
if self.identifiers is not None:
self.identifiers.append(node.id)
tree = ast.parse(sourcecode)
order = ClassOrder()
order.visit(tree)
print order.identifiers
prints out the order of assignments and methods in all class definitions. 在所有类定义中打印出分配和方法的顺序。
Demo: 演示:
>>> sourcecode = '''\
... class A(object):
... b = 1
... a = 1
... c = 1
... '''
>>> tree = ast.parse(sourcecode)
>>> order = ClassOrder()
>>> order.visit(tree)
>>> print order.identifiers
['b', 'a', 'c']
>>> tree = ast.parse(inspect.getsource(ast.NodeVisitor))
>>> order = ClassOrder()
>>> order.visit(tree)
>>> print order.identifiers
['visit', 'generic_visit']
Bundled up as a function, using inspect.getsource()
: 使用inspect.getsource()
捆绑为一个函数:
import inspect
def get_identifiers(obj):
source = inspect.getsource(obj)
tree = ast.parse(source)
order = ClassOrder()
order.visit(tree)
return order.identifiers
inspect.getsource()
can handle any object for which the source is available . inspect.getsource()
可以处理任何源可用的对象。 For classes, it'll extract just the source block defining that class, if available. 对于类,它将仅提取定义该类的源代码块(如果有)。
It is alternative inspire by @Martijn Pieters - this production code using different approach in scanning. 它是@Martijn Pieters的替代启发-此生产代码使用不同的扫描方法。
It allow scan __main__
what is problem for inspect.getsource(obj)
. 它允许扫描__main__
检查inspect.getsource(obj)
什么问题。
It collect also types for each name and function - order scan will allow filter more attributes, functions and extract more information. 它还为每个名称和功能收集类型-顺序扫描将允许筛选更多属性,功能并提取更多信息。
import ast
import sys
import codecs
import pprint
class A(object):
b = 1
a = 1
c = 1
e, f, g = (1, 2, 3)
z = x = 1
((a1, b1), (c1, d1)) = ((1, 2), (1, 2))
class B(A):
pass
class ClassOrderVisitor(ast.NodeVisitor):
def __init__(self):
self.classes = {}
def __parseTuple(self, astTuple, fields):
for element in astTuple.elts:
if isinstance(element, ast.Name):
fields.append((element.id, element))
elif isinstance(element, ast.Tuple):
self.__parseTuple(element, fields)
else:
raise NotImplementedError()
def visit_ClassDef(self, node):
fields = []
for field in ast.iter_fields(node):
# class name
if field[0] == 'name':
className = field[1]
self.classes[className] = fields
# class body
elif field[0] == 'body':
for bodyItem in field[1]:
if isinstance(bodyItem, ast.Assign):
for target in bodyItem.targets:
if isinstance(target, ast.Name):
fields.append((target.id, target))
elif isinstance(target, ast.Tuple):
self.__parseTuple(target, fields)
else:
raise NotImplementedError()
elif isinstance(bodyItem, ast.FunctionDef):
fields.append((bodyItem.name, bodyItem))
# this file is named ast_parser.py not using inspect.getsource(obj)
# since problem with __main__ scan
def scanOrder(fileName):
with codecs.open(fileName, encoding = 'utf8') as sourceFile:
sourceCode = sourceFile.read()
codeTree = ast.parse(sourceCode, fileName)
classOrderVisitor = ClassOrderVisitor()
classOrderVisitor.visit(codeTree)
return classOrderVisitor.classes
# run
pprint.pprint(scanOrder('ast_parser.py'))
print [x for x in dir(A) if not x.startswith('__') and not x.endswith('__')]
Output: 输出:
{'A': [('b', <_ast.Name object at 0x01375E70>),
('a', <_ast.Name object at 0x01375ED0>),
('c', <_ast.Name object at 0x01375F30>),
('e', <_ast.Name object at 0x01375FB0>),
('f', <_ast.Name object at 0x01375FD0>),
('g', <_ast.Name object at 0x01375FF0>),
('z', <_ast.Name object at 0x0137B0D0>),
('x', <_ast.Name object at 0x0137B0F0>),
('a1', <_ast.Name object at 0x0137B190>),
('b1', <_ast.Name object at 0x0137B1B0>),
('c1', <_ast.Name object at 0x0137B1F0>),
('d1', <_ast.Name object at 0x0137B210>)],
'B': [],
'ClassOrderVisitor': [('__init__', <_ast.FunctionDef object at 0x0137B3D0>),
('__parseTuple',
<_ast.FunctionDef object at 0x0137B4F0>),
('visit_ClassDef',
<_ast.FunctionDef object at 0x0137BA10>)]}
['a', 'a1', 'b', 'b1', 'c', 'c1', 'd1', 'e', 'f', 'g', 'x', 'z']
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.