Como você determinar onde cada propriedade e método de uma classe Python é definido?
-
20-08-2019 - |
Pergunta
Dada uma instância de alguma classe em Python, que seria útil para ser capaz de determinar qual linha de código fonte definido cada método e propriedade (por exemplo, para implementar 1 ). Por exemplo, dado um módulo ab.py
class A(object):
z = 1
q = 2
def y(self): pass
def x(self): pass
class B(A):
q = 4
def x(self): pass
def w(self): pass
definir uma função para onde (class_, atributo) retornando uma tupla contendo o nome do ficheiro, a classe, e a linha no código de fonte que definido ou subclasse attribute
. Isto significa que a definição no corpo da classe, e não a última missão devido ao dinamismo overeager. É bom se ele retorna 'desconhecido' para alguns atributos.
>>> a = A()
>>> b = B()
>>> b.spigot = 'brass'
>>> whither(a, 'z')
("ab.py", <class 'a.A'>, [line] 2)
>>> whither(b, 'q')
("ab.py", <class 'a.B'>, 8)
>>> whither(b, 'x')
("ab.py", <class 'a.B'>, 9)
>>> whither(b, 'spigot')
("Attribute 'spigot' is a data attribute")
Eu quero usar isso enquanto introspecção Plone, onde cada objeto tem centenas de métodos e seria realmente útil para classificar através deles organizados por classe e não apenas em ordem alfabética.
Claro que, em Python você pode nem sempre razoavelmente sei, mas seria bom para obter boas respostas no caso comum de código principalmente-estático.
Solução 2
Você está procurando o inspect.classify_class_attrs(cls)
função não documentada. Passá-lo uma classe e ele irá retornar uma lista de tuplas ('name', 'kind' e.g. 'method' or 'data', defining class, property)
. Se você precisar de informações sobre absolutamente tudo em uma instância específica que você terá que fazer um trabalho adicional.
Exemplo:
>>> import inspect
>>> import pprint
>>> import calendar
>>>
>>> hc = calendar.HTMLCalendar()
>>> hc.__class__.pathos = None
>>> calendar.Calendar.phobos = None
>>> pprint.pprint(inspect.classify_class_attrs(hc.__class__))
[...
('__doc__',
'data',
<class 'calendar.HTMLCalendar'>,
'\n This calendar returns complete HTML pages.\n '),
...
('__new__',
'data',
<type 'object'>,
<built-in method __new__ of type object at 0x814fac0>),
...
('cssclasses',
'data',
<class 'calendar.HTMLCalendar'>,
['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']),
('firstweekday',
'property',
<class 'calendar.Calendar'>,
<property object at 0x98b8c34>),
('formatday',
'method',
<class 'calendar.HTMLCalendar'>,
<function formatday at 0x98b7bc4>),
...
('pathos', 'data', <class 'calendar.HTMLCalendar'>, None),
('phobos', 'data', <class 'calendar.Calendar'>, None),
...
]
Outras dicas
Este é mais ou menos impossível sem uma análise estática, e mesmo assim, nem sempre vai funcionar. Você pode obter a linha onde a função foi definida e em que arquivo, examinando o seu objeto de código, mas além disso, não há muito que você pode fazer. O módulo inspect
pode ajudar com isso. Assim:
import ab
a = ab.A()
meth = a.x
# So, now we have the method.
func = meth.im_func
# And the function from the method.
code = func.func_code
# And the code from the function!
print code.co_firstlineno, code.co_filename
# Or:
import inspect
print inspect.getsource(meth), inspect.getfile(meth)
Mas considere:
def some_method(self):
pass
ab.A.some_method = some_method
ab.A.some_class_attribute = None
Ou pior:
some_cls = ab.A
some_string_var = 'another_instance_attribute'
setattr(some_cls, some_string_var, None)
Especialmente neste último caso, o que você quer ou espera receber?
Você está procurando o inspecionar módulo, especificamente inspect.getsourcefile()
e inspect.getsourcelines()
. Por exemplo
a.py:
class Hello(object):
def say(self):
print 1
>>> from a import Hello
>>> hi = Hello()
>>> inspect.getsourcefile(hi.say)
a.py
>>> inspect.getsourcelines(A, foo)
([' def say(self):\n print 1\n'], 2)
Dada a natureza dinâmica do Python, fazendo isso para situações mais complicadas pode simplesmente não ser possível ...