我正试图从matlab转向python。而魔法呢?在ipython中,Matlab的一个非常好的特性是,您可以在命令行(通过省略;)上看到相关对象的实例变量(在Matlab中称为属性)。这在Python中是可能的吗(我猜是通过i python实现的)?
理想情况下是这样的一个类:
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
Attributes:
_x: 5
x: 105
Method Attributes:
myFunction(self, y)
号
是否可以通过重写类的print方法(如果存在这样的东西)来实现?或是通过伊普敦的魔法方法?
尝试在对象名称后面写一个点,然后双击选项卡,例如myobj.。这就是你要找的那种东西吗?
这似乎只是列出了实例变量和方法,这些变量和方法很接近,但不完全相同。我还想看看实例变量的值…
您定义属性的方式很奇怪,它是一个由类属性(MyClass._x)支持的每个实例属性,直到您第一次设置它,这时您创建了一个隐藏类属性的实例属性(mc._x)。这种行为实际上是有用的,但我怀疑这是类和实例成员之间混淆的迹象。
简短的答案是,在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的类的任何实例"、"类的任何实例"、"新样式类的任何实例"或"任何类型的任何值"(模块、类、函数等)。你可以在任何事情上使用dir和getmembers;这意味着什么的确切细节在文档中描述。好的。
最后一件事是:你可能会注意到getmembers会返回你可能不感兴趣的东西,比如('__str__',。由于结果只是名称-值对,如果您只想删除__dunder__方法、_private变量等,这很容易。但通常情况下,您希望筛选"某种成员"。getmembers函数接受一个过滤参数,但文档并不能很好地解释如何使用它(除此之外,还希望您了解描述符是如何工作的)。基本上,如果您想要一个过滤器,它通常是由inspect.isfoo函数组合而成的callable、lambda x: not callable(x)或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函数,或者从中生成一个%宏,或者将其保留为一个常规函数并显式调用它。好的。
在一条评论中,您询问是否可以将其打包成一个__repr__函数,而不是试图创建一个%魔力函数或其他什么函数。好的。
如果您已经从一个根类继承了所有的类,那么这是一个好主意。您可以编写一个适用于所有类的单个__repr__(或者,如果它适用于99%的类,您可以覆盖另一个类中的__repr__),然后每当您在解释器中评估任何对象或将其打印出来时,您将得到您想要的。好的。
但是,要记住的一些事情:好的。
由于某种原因,python同时拥有__str__(如果你是print)和__repr__(如果你只是在交互提示下评估一个对象,你会得到什么)。通常,前者是一种很好的人类可读的表示,而后者是一种既可以用eval表示(也可以用交互式提示进行打字),也可以是一种简洁的尖括号形式,使您能够很好地区分对象的类型和标识。好的。
这只是惯例而不是规则,所以你可以随意打破它。但是,如果你打算打破它,你可能仍然想利用str和repr的区别,例如,让repr给你一个完整的内部信息,而str只显示有用的公共价值。好的。
更严重的是,您必须考虑repr值是如何组成的。例如,如果您使用print或repra 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 '
'.join(lines)
正确地证明属性名是最困难的部分。(我可能搞错了,因为这是未经测试的代码…)其他的事情要么简单,要么有趣(对getmembers使用不同的过滤器来查看它们的功能)。好的。好啊。
inspect.getmembers(spam)似乎非常接近我要找的(除了列表中的前18个条目)。是否可以重写类的print方法,以便获得(新编辑的)原始问题中描述的行为?
…我是说,而不是一个ipython魔法函数。有没有办法让它成为默认行为?
我可以写一个超类,在那里我重写__repr__来完成我所描述的,然后确保我的所有子类都继承自这个超类吗?
是的,如果您希望所有类都有一个超类,这是一个很好的解决方案。您可能会考虑覆盖__repr__和__str__,这样您就可以在ipython提示下评估obj时提供详细的内部信息,但在print时更简洁易读,但在其他情况下是肯定的。
通过实现_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}{} {:
'
line = f.format(str(name), ':', str(value))
# p.text(str(name) + ': ' + str(value) + '
')
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
。
很明显,在空白等方面还需要进行一些微调,但这正是我试图完成的工作。
您可以使用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
号
但是,您可能会发现,对于可能具有大量实例变量和方法的实际对象,这并不适用。
它似乎也不包括在class Dog:行和def __init__行之间声明的实例变量…
当然,对于不使用__dict__的对象(例如,因为它们有__slots__或者它们在c中实现,或者它们有一个执行上述操作之一的基类)或者具有__dict__但也有自定义__getattr__和朋友的对象,或者……
@用户1507844:这些不是实例变量,它们是类变量。(但你是对的,它不会显示出来。)