【问题标题】:Get instance variables in order in PythonPython中按顺序获取实例变量
【发布时间】:2016-10-02 03:37:06
【问题描述】:

假设我有以下课程:

class Foo(object):
    def __init__(self, value):
        self.d = value
        self.a = value
        self.s = value
        self.k = value

我要按申报顺序retrieve the instance variables

我尝试使用vars() 没有任何成功:

list(vars(Foo('value')).keys())
# ['a', 'k', 's', 'd']

我想要什么:

list(magic_method(Foo('value')).keys())
# ['d', 'a', 's', 'k']

编辑

显然,每个字段都会有不同的值。

我的目标是通过对象变量生成 XML。为了有效,XML 标签的顺序必须正确。

结合__iter__ 覆盖,我只需管理对象字典即可生成我的 XML。

我们以图书馆为例。假设你有一个班级BookDatePersonAuthorBorrower

class Book(object):
    def self.__init__()
        self._borrower = Borrower()
        self._author = Author()

class Date(object)
    def __init__(self, date):
        self._date = date

class Person(object):
    def __init__(self, name):
        self._name = name

class Author(object):
    def __init__(self):
        self._person = Person("Daniel")

class Borrower(object):
    def __init__(self):
        self._person = Person("Jack")
        self._date = Date("2016-06-02")

我想创建以下 XML:

<Book>
    <Borrower>
        <Person>Jack</Person>
        <Date>2016-06-02</Date>
    </Borrower>
    <Author>
        <Person>Daniel</Person>
    </Author>
</Book>

我知道这些类可能看起来很奇怪(比如这里的 Date),但我想让问题尽可能简单(并且有些字段非常有意义)。在实践中,我会查询数据库并可能在初始化程序中传递一个记录标识符。关键是有些数据遵循相同的语法(即此处的 Person)。

总而言之,我想使用 Python 对象创建这样的 XML。订购事项。这就是为什么我想为此目的按顺序检索变量:然后我可以提取类并生成 XML 标记。

【问题讨论】:

  • 我不相信这是可能的,因为字典不保持秩序。
  • @JonathonReinhart:元类是可能的。
  • ...keys().sorted(key=lambda x: mapdict[x])?
  • @AlexHall:查看我的编辑。 :)
  • @tripleee:除非我弄错了,list(vars(Foo('value')).keys().sorted(key=lambda x: mapdict[x])) 会产生以下错误:AttributeError: 'list' object has no attribute 'sorted'。你能说得更清楚些吗?

标签: python


【解决方案1】:

如果你想对对象变量进行排序,你可以使用类似的东西:

from collections import OrderedDict

class FooModel(object):
    def __new__(cls, *args, **kwargs):
        instance = object.__new__(cls)
        instance.__odict__ = OrderedDict()
        return instance

    def __setattr__(self, key, value):
        if key != '__odict__':
            self.__odict__[key] = value
        object.__setattr__(self, key, value)

    def keys(self):
        return self.__odict__.keys()

    def iteritems(self):
        return self.__odict__.iteritems()


class Foo(FooModel):
    def __init__(self, value):
        self.d = value
        self.a = value
        self.s = value
        self.k = value

输出:

>>> f = Foo('value')
>>> f.x = 5
>>> f.y = 10
>>> f.a = 15
>>> f2 = Foo('value')
>>> print "f.keys()", f.keys()
f.keys() ['d', 'a', 's', 'k', 'x', 'y']
>>> print "f2.keys()", f2.keys()
f2.keys() ['d', 'a', 's', 'k']
print list(f.iteritems())
[('d', 'value'), ('a', 15), ('s', 'value'), ('k', 'value'), ('x', 5), ('y', 10)]

【讨论】:

  • 这很有趣,而且似乎有效,但我收到了 DeprecationWarning: object() takes no parameters 消息。我正在使用python2.7。
【解决方案2】:

您可以实现__setattr__ 方法来为您跟踪这些事情:

#!python3
class OrderedAttrs:
    def __init__(self, d, a, s, k):
        self._order = []
        self.d = d
        self.a = a
        self.s = s
        self.k = k


    def __setattr__(self, name, value):
        super().__setattr__(name, value)

        if not name in self._order:
            self._order.append(name)

        return value

    def ordered_attrs(self, with_order=False):
        return [(k,getattr(self, k)) for k in self._order if k != '_order' or with_order]

oa = OrderedAttrs('dee', 'eh', 'ess', 'kay')
oa.foo = 'bar'
oa.baz = 'moo'

print("Default:",oa.ordered_attrs())
print("With _order:", oa.ordered_attrs(with_order=True))

【讨论】:

    【解决方案3】:

    如果不使用元类(正如@Daniel 所说),您将无法轻松做到这一点。但是,如果您想要一种产生类似效果的解决方法,您可以将所有“实例变量”存储在 collections.OrderedDict 中:

    from collections import OrderedDict
    
    
    class Foo(object):
        def __init__(self, value):
            self.instvars = OrderedDict()
            self.instvars['d'] = value
            self.instvars['a'] = value
            self.instvars['s'] = value
            self.instvars['k'] = value
    
    
    print(list(Foo('value').instvars.keys()))
    # OUT: ['d', 'a', 's', 'k']
    

    【讨论】:

    • 但是这些不能作为常规实例变量访问。
    • 当然不是,但它确实“产生了类似的效果”。这就是我写“'instance variables'”的原因(注意引号)。如果您不想编写自己的元类,这与您将得到的差不多。
    【解决方案4】:

    你可以给你的类一个类似于__dict__的方法,但是它会返回一个OrderedDict。

    from collections import OrderedDict
    
    class Foo(object):
        def __init__(self, d, a, s, k):
            self.d = d
            self.a = a
            self.s = s
            self.k = k
    
        def ordered_dict(self):
            return OrderedDict(
                (key, getattr(self, key)) for key in ('d', 'a', 's', 'k')
            )
    

    这样工作:

    >>> myfoo = Foo('bar', 1, 7, 'foo')
    >>> myfoo.ordered_dict()
    OrderedDict([('d', 'bar'),('a', 1), ('s', 7), ('k', 'foo')])
    
    >>> list(myfoo.ordered_dict())
    ['d', 'a', 's', 'k']
    

    【讨论】:

    • 不幸的是,这就是我想要获得变量实例的原因,这要感谢vars()。我将不得不输入很多变量名。 ://
    • 如果您想以特定的正确顺序获取属性,您必须在某处声明该顺序。
    猜你喜欢
    • 2010-09-11
    • 1970-01-01
    • 2021-03-28
    • 1970-01-01
    • 1970-01-01
    • 2013-03-30
    • 1970-01-01
    • 2016-11-18
    • 2014-12-08
    相关资源
    最近更新 更多