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

标签 python matlab ipython

我正在尝试从 Matlab 转移到 Python。而魔法?在 IPython 中很好,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

会显示如下内容:
mc = Myclass()
mc
<package.MyClass> <superclass1> <superclass2>

Attributes:
_x: 5
 x: 105

Method Attributes:
myFunction(self, y)

是否可以通过覆盖类的打印方法(如果存在这样的事情)来实现?还是通过 ipython 中的魔术方法?

最佳答案

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

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

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

对于简单的类,spam.__dict__通常已经足够好了。它不处理动态属性,__slots__ -based 属性、类属性、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几乎任何事情;文档中描述了这意味着什么的确切细节。

甚至最后一件事:您可能会注意到 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 %magic 函数,或者只是用它制作一个 %macro,或者只是把它作为一个常规函数并显式调用它。

在评论中,您问是否可以将其打包成 __repr__函数,而不是尝试创建 %magic 函数或其他什么。

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

但是,请记住以下几点:

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

这只是一个约定而不是规则,所以你可以随意打破它。但是,如果您要破解它,您可能仍然希望使用 str/repr区分——例如,使 repr为您提供所有内部结构的完整转储,同时 str只显示有用的公共(public)值(value)。

更严重的是,你要考虑如何repr值组成。例如,如果您 printrepr list ,你得到,有效地,'[' + ', '.join(map(repr, item))) + ']' .多行 repr 看起来很奇怪.如果你使用任何一种试图缩进嵌套集合的 pretty-print ,就像内置在 IPython 中的那种,情况会更糟。结果可能不会不可读,它只会破坏 pretty-print 旨在提供的好处。

至于您要显示的特定内容:这一切都非常简单。像这样的东西:
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 看看它们做了什么)。

关于python - 是否可以像 Matlab 一样在 IPython 中显示对象实例变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15487839/

相关文章:

Python:假与 0

python - 创建已填充一些单元格的 IPython 笔记本

python - 在 IPython 中保存工作区

python - pylab 导入后,ipython 调试器充斥着 Qt 错误

c++ - 编译cpp文件时代码不并行,c是并行的

java - 将两个椭圆拟合到 body 轮廓

python - Numpy 中的卷积比 Matlab 中的卷积慢吗?

python - Pywinauto:找不到 click() 方法

python - 如何在 Python 中生成克隆?

python - 机器学习: Predict set of numbers based on previous number