2013-03-18 46 views
4

我想从Matlab移到Python。虽然魔术?在IPython中是很好的,Matlab的一个非常好的功能是,你可以在命令行中看到(通过省略;)所讨论对象的实例变量(称为属性)。这是可能的蟒蛇(我猜通过IPython)?是否可以像Matlab那样在IPython中显示对象实例变量?

理想的情况下像这样的类:

class MyClass(object): 
    _x = 5 

    @property 
    def x(self): 
     return self._x + 100 

    @x.setter 
    def x(self, value): 
     self._x = value + 1 

    def myFunction(self, y): 
     return self.x ** 2 + y 

会显示类似:

mc = Myclass() 
mc 
<package.MyClass> <superclass1> <superclass2> 

Attributes: 
_x: 5 
x: 105 

Method Attributes: 
myFunction(self, y) 

这有可能通过重写类的印刷方法(如果这样的事情退出)?或者通过ipython中的魔术方法?

+0

尝试编写对象名称后面跟一个点,然后双击TAB,例如'MyObj中。 '。这是你寻找的那种东西吗? – Marius 2013-03-18 22:07:21

+0

这似乎只是列出了实例变量和方法,它们很接近但并不完全。我希望看到实例变量的值以及... – user1507844 2013-03-18 22:34:17

+0

您定义属性的方式很奇怪 - 它是一个由类属性('MyClass._x')支持的每实例属性...直到您首先设置它,然后创建一个影响类属性的实例属性('mc._x')。这种行为实际上可能很有用,但我怀疑这是类和实例成员之间混淆的迹象。 – abarnert 2013-03-20 19:28:26

回答

6

简短的回答是,没有办法获得Python中所有对象的属性列表,因为这些属性可以动态生成。举一个极端的例子,考虑这个类:

>>> class Spam(object): 
...  def __getattr__(self, attr): 
...   if attr.startswith('x'): 
...    return attr[1:] 
>>> spam = Spam() 
>>> spam.xeggs 
'eggs' 

即使解释器可以有人找出所有属性的列表,该列表将是无限的。

对于简单的课程,spam.__dict__往往足够好。它不处理动态属性,基于__slots__的属性,类属性,C扩展类,从上面大多数继承的属性以及所有其他类型的东西。但至少是某种东西 - 有时候,这是你想要的东西。第一个近似值,这正是你在__init__或更高版本中明确分配的内容,而没有其他内容。

为了达到旨在提高人们可读性的“一切”的最佳效果,请使用dir(spam)

如果您想尽一切努力来达到编程使用的“一切”目标,请使用inspect.getmembers(spam)。 (虽然实际上实施只是一个在CPython的2.x的周围dir包装,它可以做多,并在事实上确实在CPython的3.2+)

这些都将处理各种各样的事情,__dict__不能,并且可能会跳过__dict__中但您不想看到的内容。但它们本质上还不完整。

无论你使用什么,获取值和键都很容易。如果您使用__dict__getmembers,这是微不足道的; __dict__通常是dict,或者为了您的目的而足够接近dict,而getmembers显式返回键值对。如果您使用dir,你可以得到一个dict很容易:

{key: getattr(spam, key) for key in dir(spam)} 

最后一件事:“对象”是一个有点模糊的术语。它可以表示“从object派生的类的任何实例”,“类的任何实例”,“新样式类的任何实例”或“任何类型的任何值”(模块,类,函数,等等。)。几乎任何东西都可以使用dirgetmembers;文档中描述了这些含义的具体细节。

一个偶数最后-ER的事情:您可能注意到,getmembers回报之类的东西('__str__', <method-wrapper '__str__' of Spam object at 0x1066be790>),这你可能不感兴趣,给出的结果也只是名称 - 值对,如果你只是想删除__dunder__方法。 ,_private变量等,这很容易。但是,通常情况下,你想过滤“种类的成员”。 getmembers函数需要一个过滤器参数,但文档在解释如何使用它方面做得并不出色(并且,最重要的是,希望您了解描述符是如何工作的)。基本上,如果你想要一个过滤器,它通常是callable,lambda x: not callable(x)lambdainspect.isfoo函数的组合组成。

所以,这是很常见,你可能希望把它写了一个函数:

def get_public_variables(obj): 
    return [(name, value) for name, value 
      in inspect.getmembers(obj, lambda x: not callable(x)) 
      if not name.startswith('_')] 

你可以把它转换成一个自定义的IPython%的神奇功能,或者只是做一个%宏出来,或者把它作为一个常规函数并明确地调用它。


在一个评论,你问你是否可以只打包这件事成__repr__功能,而不是试图创建%法术功能或什么的。

如果您已经拥有了从单个根类继承的所有类,这是一个好主意。你可以编写一个适用于你所有课程的单一__repr__(或者,如果它适用于其中99%的课程,则可以在其他1%中覆盖该__repr__),然后每次评估解释器中的任何对象时或打印出来,你会得到你想要的。

然而,几件事情要记住:

Python有两个__str__(你会得到什么,如果你print的对象)和__repr__(你会得到什么,如果你只是评估在交互提示的对象)一个原因。通常,前者是一种很好的人类可读表示,而后者是eval -able(或交互式提示中的可打字形式),或者简洁的角括号形式,可以让您足以区分类型和对象的身份。

这只是一个惯例,而不是一个规则,所以你可以随意打破它。但是,如果您的要打破它,您可能仍然要使用str/repr区别 - 例如,使repr为您提供了所有内部的完整转储,而str仅显示了有用的公共值。

更严重的是,您必须考虑如何组成repr值。例如,如果您使用printrepr a list,那么实际上可以得到'[' + ', '.join(map(repr, item))) + ']'。这在多行repr中看起来很奇怪。如果你使用任何一种试图缩进嵌套集合的漂亮打印机,就像IPython内置的集合那样,情况会更糟糕。结果可能不会是无法读取的,它只会打败漂亮打印机提供的好处。

至于你想展示的具体东西:这很容易。类似这样的:

def __repr__(self): 
    lines = [] 

    classes = inspect.getmro(type(self)) 
    lines.append(' '.join(repr(cls) for cls in classes)) 

    lines.append('') 
    lines.append('Attributes:') 
    attributes = inspect.getmembers(self, callable) 
    longest = max(len(name) for name, value in attributes) 
    fmt = '{:>%s}: {}' % (longest,) 
    for name, value in attributes: 
     if not name.startswith('__'): 
      lines.append(fmt.format(name, value)) 

    lines.append('') 
    lines.append('Methods:') 
    methods = inspect.getmembers(self, negate(callable)) 
    for name, value in methods: 
     if not name.startswith('__'): 
      lines.append(name) 

    return '\n'.join(lines) 

右对齐属性名称是最难的部分。 (而且我可能弄错了,因为这是未经测试的代码...)其他一切都很简单,或者很有趣(使用不同的过滤器以getmembers来查看它们的功能)。

+0

'inspect.getmembers(垃圾邮件)'似乎与我正在寻找的内容非常接近(除了列表中的前18个条目)。是否有可能重写该类的打印方法,以便获得(新编辑的)原始问题中描述的行为? – user1507844 2013-03-20 10:55:06

+0

...而不是一个ipython魔术功能我的意思是。那么是否有办法使它成为默认行为 – user1507844 2013-03-20 11:21:50

+0

我可以直接编写一个超类,在这里我重载'__repr__'来完成我所描述的内容,然后确保我的所有子类都继承自这个超类? – user1507844 2013-03-20 11:35:44

0

你可以使用obj.__dict__对象的实例变量,例如:

class Dog: 
    def __init__(self, name, age): 
     self.name = name 
     self.age = age 


d1 = Dog("Fido", 7) 

for key, val in d1.__dict__.items(): 
    print(key, ": ", val) 

输出:

age : 7 
name : Fido 

您可能会发现这并不能很好地可能有大量实物工作但实例变量和方法的数量。

+0

它似乎还没有包含在'class Dog:'行和'def __init__'行之间声明的实例变量...... – user1507844 2013-03-18 22:49:37

+0

当然,这对于不使用'__dict__'的对象例如,因为他们有'__slots__',或者他们用C实现,或者他们有一个基类来完成上述任何一个操作),或者有'__dict__'的对象,但也有自定义的'__getattr__'和好友,或... – abarnert 2013-03-18 22:50:06

+0

@ user1507844:那些_aren't_实例变量,它们是_class_变量。 (但你是对的,它不会显示它们。) – abarnert 2013-03-18 22:50:47

1

我能够实现我通过实施_repr_pretty_与IPython中(至少粗略地)想:

def get_public_variables(obj): 
    from inspect import getmembers 
    return [(name, value) for name, value in 
      getmembers(obj, lambda x: not callable(x)) if 
      not name.startswith('__')] 


class MySuperClass(object): 
    def _repr_pretty_(self, p, cycle): 
     for (name, value) in get_public_variables(self): 
      f = '{:>12}{} {:<} \n' 
      line = f.format(str(name), ':', str(value)) 
      # p.text(str(name) + ': ' + str(value) + '\n') 
      p.text(line) 

class MyClass(MySuperClass): 
    _x = 5 

    @property 
    def x(self): 
     return self._x + 100 

给我出的:

mc = MyClass() 
mc 
Out[15]: 
      _x: 5 
      x: 105 

显然有一些微调是按照空格等来完成。但这大致是我试图完成的操作

相关问题