是否可以像Matlab那样在IPython中显示对象实例变量?

是否可以像Matlab那样在IPython中显示对象实例变量?,python,matlab,ipython,Python,Matlab,Ipython,我正试图从Matlab转向Python。而魔法呢?在IPython is nice中,Matlab的一个非常好的特性是,您可以在命令行(省略;)上看到所讨论对象的实例变量(在Matlab中称为属性)。这在python中可能吗(我想是通过IPython实现的) 理想情况下,像这样的类: class MyClass(object): _x = 5 @property def x(self): return self._x + 100 @x.sett

我正试图从Matlab转向Python。而魔法呢?在IPython is nice中,Matlab的一个非常好的特性是,您可以在命令行(省略;)上看到所讨论对象的实例变量(在Matlab中称为属性)。这在python中可能吗(我想是通过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
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)
将显示如下内容:

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

Attributes:
_x: 5
 x: 105

Method Attributes:
myFunction(self, y)
mc=Myclass()
司仪
属性:
_x:5
x:105
方法属性:
myFunction(自我,y)

是否可以通过重写类的print方法(如果存在这样的方法)实现这一点?或者通过ipython?

中的神奇方法,您可以使用
obj获取对象的实例变量。例如:

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

您可能会发现,对于可能有大量实例变量和方法的真实对象,这并不适用。

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

>>> class Spam(object):
...     def __getattr__(self, attr):
...         if attr.startswith('x'):
...             return attr[1:]
>>> spam = Spam()
>>> spam.xeggs
'eggs'
即使解释器能够找出所有属性的列表,该列表也是无限的

对于简单的类,
spam.\uuu dict\uu
通常就足够了。它不处理动态属性、基于
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>的属性、类属性、C扩展类、从上述大部分继承的属性以及所有其他类型的东西。但它至少是某种东西,有时,它是你想要的东西。第一个近似值是,它正是您在
\uuuu init\uuuu
或更高版本中显式指定的内容,而不是其他内容

为了尽最大努力实现“一切”的可读性,请使用

为了尽最大努力实现“一切”的程序化使用,请使用。(虽然实际上,该实现只是CPython 2.x中
dir
的包装,但它可以做得更多,事实上在CPython 3.2+中也可以做到。)

它们既可以处理
\uuuuu dict\uuuu
无法处理的大量内容,也可以跳过
\uuuu dict\uuuu
中您不想看到的内容。但它们本质上仍然是不完整的

无论您使用什么,获取值和键都很容易。如果您使用的是
\uuu dict\uuu
getmembers
,那么它很简单;
\uuuu dict\uuuu
通常是一个
dict
,或者是一个与
dict
非常接近的对象,并且
getmembers
显式返回键值对。如果您使用的是
dir
,您可以非常轻松地获得
dict

{key: getattr(spam, key) for key in dir(spam)}
最后一件事:“对象”是一个模糊的术语。它可以表示“从
对象派生的类的任何实例”、“类的任何实例”、“新样式类的任何实例”或“任何类型的任何值”(模块、类、函数等)。你可以在任何事情上使用
dir
getmembers
;文件中详细描述了这意味着什么

还有最后一件事:您可能会注意到,
getmembers
返回像
(“str”、
)这样的东西,您可能对此不感兴趣。由于结果只是名称-值对,如果您只想删除
\uu\dunder\uu
方法、
\u private
变量等,这很容易。但通常,您希望筛选“类型的成员”。
getmembers
函数接受一个过滤器参数,但是文档并没有很好地解释如何使用它(最重要的是,希望您了解描述符是如何工作的)。基本上,如果您想要一个过滤器,它通常是
可调用的
lambda x:不可调用的(x)
,或者由
inspect.isfoo
函数组合而成的
lambda

因此,这很常见,您可能希望将其作为函数编写:

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%magic函数,或者只使用它生成一个%宏,或者只将其作为常规函数并显式调用


在一篇评论中,您询问是否可以将其打包成一个
\uuu repr\uuu
函数,而不是尝试创建一个%magic函数或其他任何函数

如果您已经从一个根类继承了所有类,那么这是一个好主意。您可以编写一个适用于所有类的
\uuuuu repr\uuuuuu
(或者,如果它适用于99%的类,您可以在其他1%中重写该
\uuuu repr\uuuuu
),然后每次在解释器中计算任何对象或打印它们时,您都会得到想要的结果

但是,要记住以下几点:

Python既有
\uuuu str\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。通常,前者是一种很好的人类可读的表示形式,而后者要么是
eval
-able(或可在交互提示中键入),要么是简洁的尖括号形式,让您刚好能够区分对象的类型和标识

这只是一个惯例而不是规则,所以你可以随意打破它。但是,如果您打算破坏它,您可能仍然希望利用
str
/
repr
的区别,例如,make
repr
为您提供所有内部文件的完整转储,而
str
只显示有用的公共值

更严重的是,你必须考虑<代码> RPRP/COD>值是如何组成的。例如,如果您
print
repr
一个
列表
,您将得到有效的
'['+','.join(map(repr,item)))+']'
。如果使用多行
repr
,这看起来会很奇怪。如果你使用任何一种漂亮的打印机来缩进嵌套的集合,比如
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