可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
inspect.getmembers(object[, predicate])
Return all the members of an object in a list of (name, value) pairs sorted by name.
I want to use this method, but I don't want the members to be sorted. I want them returned in the same order they were defined. Is there an alternative to this method?
Use case:
Creating a form like so:
class RegisterForm(Form):
username = Field(model_field='username', filters=validators.minlength(3))
password1 = Field(model_field='password', widget=widgets.PasswordInput)
password2 = Field(widget=widgets.PasswordInput)
first_name = Field(model_field='first_name')
last_name = Field(model_field='last_name')
address = SubForm(form=AddressForm, model_field='address')
I want the fields to be rendered in the same order they are defined.
回答1:
You can dig around to find the line number for methods, not sure about other members:
import inspect
class A:
def one(self):
pass
def two(self):
pass
def three(self):
pass
def four(self):
pass
def linenumber_of_member(m):
try:
return m[1].im_func.func_code.co_firstlineno
except AttributeError:
return -1
a = A()
l = inspect.getmembers(a)
print l
l.sort(key=linenumber_of_member)
print l
prints:
[('__doc__', None), ('__module__', '__main__'), ('four', <bound method A.four of <__main__.A instance at 0x0179F738>>), ('one', <bound method A.one of <__main__.A instance at 0x0179F738>>), ('three', <bound method A.three of <__main__.A instance at 0x0179F738>>), ('two', <bound method A.two of <__main__.A instance at 0x0179F738>>)]
[('__doc__', None), ('__module__', '__main__'), ('one', <bound method A.one of <__main__.A instance at 0x0179F738>>), ('two', <bound method A.two of <__main__.A instance at 0x0179F738>>), ('three', <bound method A.three of <__main__.A instance at 0x0179F738>>), ('four', <bound method A.four of <__main__.A instance at 0x0179F738>>)]
回答2:
The attributes (methods and other members) of an object is usually looked up through an object's special __dict__
attribute which is a standard Python dictionary. It doesn't guarantee any specific ordering.
If an attribute is not found in the object's __dict__
the class's is searched instead (where methods usually reside) and so on until the whole inheritance chain has been traversed.
Here is some custom inspection done in the interactive prompt to illustrate this (Python 3.1):
>>> class Klass():
... def test(self):
... pass
...
>>> k = Klass()
>>> k.__dict__
{}
>>> k.__class__.__dict__.items()
[('test', <function test at 0x00000000024113C8>), ('__dict__', <attribute '__dic
t__' of 'Klass' objects>), ('__module__', '__main__'), ('__weakref__', <attribut
e '__weakref__' of 'Klass' objects>), ('__doc__', None)]
Would I have put a constructor (__init__
) in Klass and set an attribute through self
it would've shown up in k.__dict__
.
You can circumvent this by using a custom metaclass. The documentation contains an example which does exactly what you want.
See the bottom of this page for the OrderedClass example.
Don't know what version of Python you have so I assumed latest.
回答3:
I don't think Python 2.6 has a __prepare__
method, so I can't swap out the default dict
for an ordered one. I can, however, replace it using a metaclass
and the __new__
method. Instead of inspecting line numbers, I think its easier and more efficient to just use a creation counter.
class MetaForm(type):
def __new__(cls, name, bases, attrs):
attrs['fields'] = OrderedDict(
sorted(
[(name, attrs.pop(name)) for name, field in attrs.items() if isinstance(field, Field)],
key=lambda t: t[1].counter
)
)
return type.__new__(cls, name, bases, attrs)
class Form(object):
__metaclass__ = MetaForm
class Field(object):
counter = 0
def __init__(self):
self.counter = Field.counter
Field.counter += 1
回答4:
members = []
for name, obj in inspect.getmembers(module):
source, start_line = inspect.getsourcelines(obj)
members.append([name, obj, start_line])
def _line_order(value):
return value[2]
members.sort(key = _line_order)
回答5:
In reference to Ned Batchelder's answer above, in Python 3 line numbers of a method m can be gotten with m.__func__.__code__.co_firstlineno
inspect
— Inspect live objects