标签归档:magic-methods

python numpy.where()如何工作?

问题:python numpy.where()如何工作?

我正在玩耍numpy并浏览文档,并且遇到了一些魔术。即我正在谈论numpy.where()

>>> x = np.arange(9.).reshape(3, 3)
>>> np.where( x > 5 )
(array([2, 2, 2]), array([0, 1, 2]))

它们如何在内部实现您能够将类似的东西传递x > 5给方法的功能?我想这与它有关,__gt__但是我正在寻找详细的解释。

I am playing with numpy and digging through documentation and I have come across some magic. Namely I am talking about numpy.where():

>>> x = np.arange(9.).reshape(3, 3)
>>> np.where( x > 5 )
(array([2, 2, 2]), array([0, 1, 2]))

How do they achieve internally that you are able to pass something like x > 5 into a method? I guess it has something to do with __gt__ but I am looking for a detailed explanation.


回答 0

他们如何在内部实现将x> 5之类的内容传递给方法的能力?

简短的答案是他们没有。

对numpy数组进行的任何逻辑运算都会返回布尔数组。(即__gt__,,__lt__等等都返回给定条件为true的布尔数组)。

例如

x = np.arange(9).reshape(3,3)
print x > 5

Yield:

array([[False, False, False],
       [False, False, False],
       [ True,  True,  True]], dtype=bool)

这就是为什么类似的东西if x > 5:如果x是一个numpy数组会引发ValueError的原因。它是True / False值的数组,而不是单个值。

此外,numpy数组可以由布尔数组索引。例如,在这种情况下,x[x>5]yields [6 7 8]

老实说,您实际需要的很少,numpy.where但它只返回布尔数组为的索引True。通常,您可以使用简单的布尔索引来完成所需的操作。

How do they achieve internally that you are able to pass something like x > 5 into a method?

The short answer is that they don’t.

Any sort of logical operation on a numpy array returns a boolean array. (i.e. __gt__, __lt__, etc all return boolean arrays where the given condition is true).

E.g.

x = np.arange(9).reshape(3,3)
print x > 5

yields:

array([[False, False, False],
       [False, False, False],
       [ True,  True,  True]], dtype=bool)

This is the same reason why something like if x > 5: raises a ValueError if x is a numpy array. It’s an array of True/False values, not a single value.

Furthermore, numpy arrays can be indexed by boolean arrays. E.g. x[x>5] yields [6 7 8], in this case.

Honestly, it’s fairly rare that you actually need numpy.where but it just returns the indicies where a boolean array is True. Usually you can do what you need with simple boolean indexing.


回答 1

旧答案, 这有点令人困惑。它为您提供了陈述正确的位置(所有位置)。

所以:

>>> a = np.arange(100)
>>> np.where(a > 30)
(array([31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
       48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
       65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81,
       82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98,
       99]),)
>>> np.where(a == 90)
(array([90]),)

a = a*40
>>> np.where(a > 1000)
(array([26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
       43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
       60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
       77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93,
       94, 95, 96, 97, 98, 99]),)
>>> a[25]
1000
>>> a[26]
1040

我将它用作list.index()的替代方法,但它还有许多其他用途。我从未将其用于2D阵列。

http://docs.scipy.org/doc/numpy/reference/generated/numpy.where.html

新答案 似乎这个人在问一些更基本的问题。

问题是您如何实现允许功能(例如在哪里)知道所请求内容的东西。

首先请注意,调用任何比较运算符都会做一件有趣的事情。

a > 1000
array([False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True`,  True,  True,  True,  True,  True,  True,  True,  True,  True], dtype=bool)`

这是通过重载“ __gt__”方法来完成的。例如:

>>> class demo(object):
    def __gt__(self, item):
        print item


>>> a = demo()
>>> a > 4
4

如您所见,“ a> 4”是有效代码。

您可以在此处获得所有重载函数的完整列表和文档:http : //docs.python.org/reference/datamodel.html

令人难以置信的是,这样做非常简单。python中的所有操作都是以这种方式完成的。说a> b等于a。gt(b)!

Old Answer it is kind of confusing. It gives you the LOCATIONS (all of them) of where your statment is true.

so:

>>> a = np.arange(100)
>>> np.where(a > 30)
(array([31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
       48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
       65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81,
       82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98,
       99]),)
>>> np.where(a == 90)
(array([90]),)

a = a*40
>>> np.where(a > 1000)
(array([26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
       43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
       60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
       77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93,
       94, 95, 96, 97, 98, 99]),)
>>> a[25]
1000
>>> a[26]
1040

I use it as an alternative to list.index(), but it has many other uses as well. I have never used it with 2D arrays.

http://docs.scipy.org/doc/numpy/reference/generated/numpy.where.html

New Answer It seems that the person was asking something more fundamental.

The question was how could YOU implement something that allows a function (such as where) to know what was requested.

First note that calling any of the comparison operators do an interesting thing.

a > 1000
array([False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True`,  True,  True,  True,  True,  True,  True,  True,  True,  True], dtype=bool)`

This is done by overloading the “__gt__” method. For instance:

>>> class demo(object):
    def __gt__(self, item):
        print item


>>> a = demo()
>>> a > 4
4

As you can see, “a > 4” was valid code.

You can get a full list and documentation of all overloaded functions here: http://docs.python.org/reference/datamodel.html

Something that is incredible is how simple it is to do this. ALL operations in python are done in such a way. Saying a > b is equivalent to a.gt(b)!


回答 2

np.where返回一个元组,其长度等于在其上被调用的numpy ndarray的维数(换句话说ndim),并且元组的每个项目都是一个初始ndarray中条件为True的所有值的索引的numpy ndarray。(请不要将尺寸与形状混淆)

例如:

x=np.arange(9).reshape(3,3)
print(x)
array([[0, 1, 2],
      [3, 4, 5],
      [6, 7, 8]])
y = np.where(x>4)
print(y)
array([1, 2, 2, 2], dtype=int64), array([2, 0, 1, 2], dtype=int64))


y是长度为2的元组,因为x.ndim为2。元组的第一项包含所有大于4的元素的行号,第二项包含所有大于4的元素的列号。如您所见,[1,2,2 ,2]对应于5,6,7,8的行号,[2,0,1,2]对应于5,6,7,8的列号注意,ndarray沿第一维(行方向)遍历)。

同样,

x=np.arange(27).reshape(3,3,3)
np.where(x>4)


将返回长度为3的元组,因为x具有3个维度。

但是,等等,np.where还有更多!

当两个附加参数被添加到np.where; 它将对上述元组获得的所有那些成对的行-列组合执行替换操作。

x=np.arange(9).reshape(3,3)
y = np.where(x>4, 1, 0)
print(y)
array([[0, 0, 0],
   [0, 0, 1],
   [1, 1, 1]])

np.where returns a tuple of length equal to the dimension of the numpy ndarray on which it is called (in other words ndim) and each item of tuple is a numpy ndarray of indices of all those values in the initial ndarray for which the condition is True. (Please don’t confuse dimension with shape)

For example:

x=np.arange(9).reshape(3,3)
print(x)
array([[0, 1, 2],
      [3, 4, 5],
      [6, 7, 8]])
y = np.where(x>4)
print(y)
array([1, 2, 2, 2], dtype=int64), array([2, 0, 1, 2], dtype=int64))


y is a tuple of length 2 because x.ndim is 2. The 1st item in tuple contains row numbers of all elements greater than 4 and the 2nd item contains column numbers of all items greater than 4. As you can see, [1,2,2,2] corresponds to row numbers of 5,6,7,8 and [2,0,1,2] corresponds to column numbers of 5,6,7,8 Note that the ndarray is traversed along first dimension(row-wise).

Similarly,

x=np.arange(27).reshape(3,3,3)
np.where(x>4)


will return a tuple of length 3 because x has 3 dimensions.

But wait, there’s more to np.where!

when two additional arguments are added to np.where; it will do a replace operation for all those pairwise row-column combinations which are obtained by the above tuple.

x=np.arange(9).reshape(3,3)
y = np.where(x>4, 1, 0)
print(y)
array([[0, 0, 0],
   [0, 0, 1],
   [1, 1, 1]])

为什么Python使用“魔术方法”?

问题:为什么Python使用“魔术方法”?

我最近一直在使用Python,而我发现有点奇怪的是,广泛使用了“魔术方法”,例如,使其长度可用,一个对象实现一个方法,def __len__(self)然后在你写len(obj)

我只是想知道为什么对象不能简单地定义一个len(self)方法并直接将其作为对象的成员来调用,例如obj.len()?我敢肯定,Python这么做的确有充分的理由,但是作为一个新手,我还没有弄清楚它们到底是什么。

I’ve been playing around with Python recently, and one thing I’m finding a bit odd is the extensive use of ‘magic methods’, e.g. to make its length available, an object implements a method, def __len__(self), and then it is called when you write len(obj).

I was just wondering why objects don’t simply define a len(self) method and have it called directly as a member of the object, e.g. obj.len()? I’m sure there must be good reasons for Python doing it the way it does, but as a newbie I haven’t worked out what they are yet.


回答 0

AFAIK len在这方面很特别,并且具有历史渊源。

这是FAQ中的报价:

为什么Python使用方法来实现某些功能(例如list.index()),却使用其他方法(例如len(list))呢?

主要原因是历史。函数用于那些对一组类型通用的操作,即使对于根本没有方法的对象(例如,元组),这些功能也可以使用。使用Python的功能部件(map(),apply()等)时,具有可以轻松应用于对象的不定形集合的函数也很方便。

实际上,将len(),max(),min()实现为内置函数实际上比将它们实现为每种类型的方法要少。人们可能会质疑个别情况,但这是Python的一部分,现在进行这样的基本更改为时已晚。必须保留功能以避免大量代码损坏。

其他“魔术方法”(在Python民俗中实际上称为特殊方法)很有道理,其他语言中也存在类似的功能。它们通常用于使用特殊语法时隐式调用的代码。

例如:

  • 重载运算符(存在于C ++和其他语言中)
  • 构造函数/析构函数
  • 用于访问属性的挂钩
  • 元编程工具

等等…

AFAIK, len is special in this respect and has historical roots.

Here’s a quote from the FAQ:

Why does Python use methods for some functionality (e.g. list.index()) but functions for other (e.g. len(list))?

The major reason is history. Functions were used for those operations that were generic for a group of types and which were intended to work even for objects that didn’t have methods at all (e.g. tuples). It is also convenient to have a function that can readily be applied to an amorphous collection of objects when you use the functional features of Python (map(), apply() et al).

In fact, implementing len(), max(), min() as a built-in function is actually less code than implementing them as methods for each type. One can quibble about individual cases but it’s a part of Python, and it’s too late to make such fundamental changes now. The functions have to remain to avoid massive code breakage.

The other “magical methods” (actually called special method in the Python folklore) make lots of sense, and similar functionality exists in other languages. They’re mostly used for code that gets called implicitly when special syntax is used.

For example:

  • overloaded operators (exist in C++ and others)
  • constructor/destructor
  • hooks for accessing attributes
  • tools for metaprogramming

and so on…


回答 1

从Python的Zen中:

面对模棱两可的想法,拒绝猜测的诱惑。
应该有一种-最好只有一种-显而易见的方法。

这是原因之一-自定义方法,开发人员可以自由选择不同的方法的名称,如getLength()length()getlength()或任何责任。Python强制执行严格的命名,以便len()可以使用通用功能。

这是常见的许多类型的对象,所有的操作都投入到神奇的方法,比如__nonzero____len__或者__repr__。不过,它们大多是可选的。

运算符重载也可以通过魔术方法(例如__le__)完成,因此也可以将它们用于其他常见操作。

From the Zen of Python:

In the face of ambiguity, refuse the temptation to guess.
There should be one– and preferably only one –obvious way to do it.

This is one of the reasons – with custom methods, developers would be free to choose a different method name, like getLength(), length(), getlength() or whatsoever. Python enforces strict naming so that the common function len() can be used.

All operations that are common for many types of objects are put into magic methods, like __nonzero__, __len__ or __repr__. They are mostly optional, though.

Operator overloading is also done with magic methods (e.g. __le__), so it makes sense to use them for other common operations, too.


回答 2

Python使用“魔术方法”一词,因为这些方法确实可以为您的程序带来魔术。使用Python的魔术方法的最大优点之一是,它们提供了一种使对象表现为内置类型的简单方法。这意味着您可以避免执行基本运算符的丑陋,违反直觉和非标准的方式。

考虑以下示例:

dict1 = {1 : "ABC"}
dict2 = {2 : "EFG"}

dict1 + dict2
Traceback (most recent call last):
  File "python", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'dict' and 'dict'

这会产生错误,因为字典类型不支持加法。现在,让我们扩展字典类并添加“ __add__”魔术方法:

class AddableDict(dict):

    def __add__(self, otherObj):
        self.update(otherObj)
        return AddableDict(self)


dict1 = AddableDict({1 : "ABC"})
dict2 = AddableDict({2 : "EFG"})

print (dict1 + dict2)

现在,它给出以下输出。

{1: 'ABC', 2: 'EFG'}

因此,通过添加此方法,突然发生了魔术,而您早些时候遇到的错误也消失了。

我希望,这使您明白了。有关更多信息,请参阅:

Python魔术方法指南(Rafe Kettler,2012)

Python uses the word “magic methods”, because those methods really performs magic for you program. One of the biggest advantages of using Python’s magic methods is that they provide a simple way to make objects behave like built-in types. That means you can avoid ugly, counter-intuitive, and nonstandard ways of performing basic operators.

Consider a following example:

dict1 = {1 : "ABC"}
dict2 = {2 : "EFG"}

dict1 + dict2
Traceback (most recent call last):
  File "python", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'dict' and 'dict'

This gives an error, because the dictionary type doesn’t support addition. Now, let’s extend dictionary class and add “__add__” magic method:

class AddableDict(dict):

    def __add__(self, otherObj):
        self.update(otherObj)
        return AddableDict(self)


dict1 = AddableDict({1 : "ABC"})
dict2 = AddableDict({2 : "EFG"})

print (dict1 + dict2)

Now, it gives following output.

{1: 'ABC', 2: 'EFG'}

Thus, by adding this method, suddenly magic has happened and the error you were getting earlier, has gone away.

I hope, it makes things clear to you. For more information, refer to:

A Guide to Python’s Magic Methods (Rafe Kettler, 2012)


回答 3

这些功能中的某些功能不仅仅可以实现单个方法(在超类上没有抽象方法)也可以实现。例如,bool()行为类似于:

def bool(obj):
    if hasattr(obj, '__nonzero__'):
        return bool(obj.__nonzero__())
    elif hasattr(obj, '__len__'):
        if obj.__len__():
            return True
        else:
            return False
    return True

您还可以100%确定bool()将始终返回True或False;如果您依靠一种方法,则不能完全确定自己会得到什么。

具有相对复杂的实现一些其他功能(不是底层魔术方法更加复杂,很可能是)是iter()cmp(),和所有的属性的方法(getattrsetattrdelattr)。诸如此类的事物int在执行强制时也可以访问魔术方法(您可以实现__int__),但是作为类型承担双重责任。 len(obj)实际上是一种我认为与没什么不同的情况obj.__len__()

Some of these functions do more than a single method would be able to implement (without abstract methods on a superclass). For instance bool() acts kind of like this:

def bool(obj):
    if hasattr(obj, '__nonzero__'):
        return bool(obj.__nonzero__())
    elif hasattr(obj, '__len__'):
        if obj.__len__():
            return True
        else:
            return False
    return True

You can also be 100% sure that bool() will always return True or False; if you relied on a method you couldn’t be entirely sure what you’d get back.

Some other functions that have relatively complicated implementations (more complicated than the underlying magic methods are likely to be) are iter() and cmp(), and all the attribute methods (getattr, setattr and delattr). Things like int also access magic methods when doing coercion (you can implement __int__), but do double duty as types. len(obj) is actually the one case where I don’t believe it’s ever different from obj.__len__().


回答 4

它们并不是真正的“魔术名称”。它只是对象必须实现以提供给定服务的接口。从这个意义上讲,它们没有比您必须重新实现的任何预定义接口定义更神奇。

They are not really “magic names”. It’s just the interface an object has to implement to provide a given service. In this sense, they are not more magic than any predefined interface definition you have to reimplement.


回答 5

尽管原因主要是历史性的,但Python的特性有其特殊之处 len使得使用函数而不是适当的方法成为可能。

Python中的某些操作被实现为方法,例如list.indexdict.append,而另一些则是可调用和神奇的方法来实现,例如striterreversed。两组之间的差异足够大,因此可以采用不同的方法:

  1. 它们很常见。
  2. strint而朋友是类型。调用构造函数更有意义。
  3. 实现与函数调用不同。例如,iter可能__getitem__在if __iter__不可用时调用,并支持方法调用中不适合的其他参数。出于相同的原因it.next(),已更改为next(it)在最新版本的Python中 -更有意义。
  4. 其中一些是运营商的近亲。有用于调用的语法__iter____next__-称为for循环。为了一致性,功能更好。而且,它对于某些优化来说更好。
  5. 有些功能在某种程度上与其余功能过于相似- repr行为相似str。有str(x)x.repr()将造成混乱。
  6. 其中有些很少使用实际的实现方法,例如 isinstance
  7. 其中一些是实际操作员,getattr(x, 'a')是另一种操作方式,x.a并且getattr具有许多上述质量。

我个人称第一类为方法,第二类为运算符。这不是一个很好的区别,但我希望它能有所帮助。

话虽如此,len这并不完全适合第二组。它与第一个操作更接近,唯一的区别是,它比几乎所有操作都更常见。但是它唯一要做的就是调用__len__,并且非常接近L.index。但是,有一些差异。例如,__len__可能会调用其他功能的实现,例如bool,如果调用了该方法,则len可能会bool(x)与customlen做完全不同的事情的方法。

简而言之,您可以拥有一组类可能实现的非常通用的功能,这些功能可以通过操作员,在对象构造期间通过特殊功能(通常比实现操作员做得更多)执行的特殊功能访问,并且在所有这些操作中具有一些共同的特征。其余所有方法。这len是该规则的一个exceptions。

While the reason is mostly historic, there are some peculiarities in Python’s len that make the use of a function instead of a method appropriate.

Some operations in Python are implemented as methods, for example list.index and dict.append, while others are implemented as callables and magic methods, for example str and iter and reversed. The two groups differ enough so the different approach is justified:

  1. They are common.
  2. str, int and friends are types. It makes more sense to call the constructor.
  3. The implementation differs from the function call. For example, iter might call __getitem__ if __iter__ isn’t available, and supports additional arguments that don’t fit in a method call. For the same reason it.next() has been changed to next(it) in recent versions of Python – it makes more sense.
  4. Some of these are close relatives of operators. There’s syntax for calling __iter__ and __next__ – it’s called the for loop. For consistency, a function is better. And it makes it better for certain optimisations.
  5. Some of the functions are simply way too similar to the rest in some way – repr acts like str does. Having str(x) versus x.repr() would be confusing.
  6. Some of them rarely use the actual implementation method, for example isinstance.
  7. Some of them are actual operators, getattr(x, 'a') is another way of doing x.a and getattr shares many of the aforementioned qualities.

I personally call the first group method-like and the second group operator-like. It’s not a very good distinction, but I hope it helps somehow.

Having said this, len doesn’t exactly fit in the second group. It’s more close to the operations in the first one, with the only difference that it’s way more common than almost any of them. But the only thing that it does is calling __len__, and it’s very close to L.index. However, there are some differences. For example, __len__ might be called for the implementation of other features, such as bool, if the method was called len you might break bool(x) with custom len method that does completely different thing.

In short, you have a set of very common features that classes might implement that might be accessed through an operator, through a special function (that usually does more than the implementation, as an operator would), during object construction, and all of them share some common traits. All the rest is a method. And len is somewhat of an exception to that rule.


回答 6

上面的两个帖子没有太多补充,但是所有的“魔术”功能根本不是魔术。它们是__builtins__模块的一部分,该模块在解释器启动时隐式/自动导入。即:

from __builtins__ import *

程序每次启动之前都会发生。

我一直认为,如果Python仅对交互式外壳执行此操作,并且需要脚本从所需的内置文件中导入各个部分,那会更正确。同样,不同的__ main__处理在shell还是交互式中会更好。无论如何,请检查所有功能,看看没有它们的情况是什么:

dir (__builtins__)
...
del __builtins__

There is not a lot to add to the above two posts, but all the “magic” functions are not really magic at all. They are part of the __ builtins__ module which is implicitly/automatically imported when the interpreter starts. I.e.:

from __builtins__ import *

happens every time before your program starts.

I always thought it would be more correct if Python only did this for the interactive shell, and required scripts to import the various parts from builtins they needed. Also probably different __ main__ handling would be nice in shells vs interactive. Anyway, check out all the functions, and see what it is like without them:

dir (__builtins__)
...
del __builtins__

Python __call__特殊方法的实际示例

问题:Python __call__特殊方法的实际示例

我知道__call__调用类的实例时会触发类中的方法。但是,我不知道何时可以使用这种特殊方法,因为一个人可以简单地创建一个新方法并执行在__call__方法中完成的相同操作,而无需调用实例,而可以调用该方法。

如果有人给我这种特殊方法的实际用法,我将不胜感激。

I know that __call__ method in a class is triggered when the instance of a class is called. However, I have no idea when I can use this special method, because one can simply create a new method and perform the same operation done in __call__ method and instead of calling the instance, you can call the method.

I would really appreciate it if someone gives me a practical usage of this special method.


回答 0

Django表单模块__call__很好地使用了方法来实现用于表单验证的一致API。您可以在Django中将自己的验证器作为函数编写。

def custom_validator(value):
    #your validation logic

Django有一些默认的内置验证器,例如电子邮件验证器,URL验证器等,它们大体上属于RegEx验证器。为了清晰地实现这些,Django求助于可调用类(而不是函数)。它在RegexValidator中实现默认的Regex验证逻辑,然后扩展这些类以进行其他验证。

class RegexValidator(object):
    def __call__(self, value):
        # validation logic

class URLValidator(RegexValidator):
    def __call__(self, value):
        super(URLValidator, self).__call__(value)
        #additional logic

class EmailValidator(RegexValidator):
    # some logic

现在,可以使用相同的语法调用自定义函数和内置的EmailValidator。

for v in [custom_validator, EmailValidator()]:
    v(value) # <-----

如您所见,Django中的此实现类似于其他人在下面的答案中解释的实现。可以通过其他任何方式实现吗?您可以,但是恕我直言,对于像Django这样的大型框架,它不那么易读或易于扩展。

Django forms module uses __call__ method nicely to implement a consistent API for form validation. You can write your own validator for a form in Django as a function.

def custom_validator(value):
    #your validation logic

Django has some default built-in validators such as email validators, url validators etc., which broadly fall under the umbrella of RegEx validators. To implement these cleanly, Django resorts to callable classes (instead of functions). It implements default Regex Validation logic in a RegexValidator and then extends these classes for other validations.

class RegexValidator(object):
    def __call__(self, value):
        # validation logic

class URLValidator(RegexValidator):
    def __call__(self, value):
        super(URLValidator, self).__call__(value)
        #additional logic

class EmailValidator(RegexValidator):
    # some logic

Now both your custom function and built-in EmailValidator can be called with the same syntax.

for v in [custom_validator, EmailValidator()]:
    v(value) # <-----

As you can see, this implementation in Django is similar to what others have explained in their answers below. Can this be implemented in any other way? You could, but IMHO it will not be as readable or as easily extensible for a big framework like Django.


回答 1

本示例使用备忘录,基本上将值存储在表(在这种情况下为字典)中,因此您以后可以查找它们,而无需重新计算它们。

在这里,我们使用带有__call__方法的简单类(通过可调用对象)来计算阶乘,而不是包含静态变量的阶乘函数(这在Python中是不可能的)。

class Factorial:
    def __init__(self):
        self.cache = {}
    def __call__(self, n):
        if n not in self.cache:
            if n == 0:
                self.cache[n] = 1
            else:
                self.cache[n] = n * self.__call__(n-1)
        return self.cache[n]

fact = Factorial()

现在,您拥有一个fact可以调用的对象,就像其他所有函数一样。例如

for i in xrange(10):                                                             
    print("{}! = {}".format(i, fact(i)))

# output
0! = 1
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
7! = 5040
8! = 40320
9! = 362880

而且它也是有状态的。

This example uses memoization, basically storing values in a table (dictionary in this case) so you can look them up later instead of recalculating them.

Here we use a simple class with a __call__ method to calculate factorials (through a callable object) instead of a factorial function that contains a static variable (as that’s not possible in Python).

class Factorial:
    def __init__(self):
        self.cache = {}
    def __call__(self, n):
        if n not in self.cache:
            if n == 0:
                self.cache[n] = 1
            else:
                self.cache[n] = n * self.__call__(n-1)
        return self.cache[n]

fact = Factorial()

Now you have a fact object which is callable, just like every other function. For example

for i in xrange(10):                                                             
    print("{}! = {}".format(i, fact(i)))

# output
0! = 1
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
7! = 5040
8! = 40320
9! = 362880

And it is also stateful.


回答 2

我发现它很有用,因为它允许我创建易于使用的API(您有一些需要一些特定参数的可调用对象),并且易于实现,因为您可以使用面向对象的实践。

以下是我昨天编写的代码,该代码使hashlib.foo散列整个文件而不是字符串的方法的版本变了:

# filehash.py
import hashlib


class Hasher(object):
    """
    A wrapper around the hashlib hash algorithms that allows an entire file to
    be hashed in a chunked manner.
    """
    def __init__(self, algorithm):
        self.algorithm = algorithm

    def __call__(self, file):
        hash = self.algorithm()
        with open(file, 'rb') as f:
            for chunk in iter(lambda: f.read(4096), ''):
                hash.update(chunk)
        return hash.hexdigest()


md5    = Hasher(hashlib.md5)
sha1   = Hasher(hashlib.sha1)
sha224 = Hasher(hashlib.sha224)
sha256 = Hasher(hashlib.sha256)
sha384 = Hasher(hashlib.sha384)
sha512 = Hasher(hashlib.sha512)

此实现使我能够以类似于功能的方式使用hashlib.foo功能:

from filehash import sha1
print sha1('somefile.txt')

当然,我可以用其他方式实现它,但是在这种情况下,这似乎是一种简单的方法。

I find it useful because it allows me to create APIs that are easy to use (you have some callable object that requires some specific arguments), and are easy to implement because you can use Object Oriented practices.

The following is code I wrote yesterday that makes a version of the hashlib.foo methods that hash entire files rather than strings:

# filehash.py
import hashlib


class Hasher(object):
    """
    A wrapper around the hashlib hash algorithms that allows an entire file to
    be hashed in a chunked manner.
    """
    def __init__(self, algorithm):
        self.algorithm = algorithm

    def __call__(self, file):
        hash = self.algorithm()
        with open(file, 'rb') as f:
            for chunk in iter(lambda: f.read(4096), ''):
                hash.update(chunk)
        return hash.hexdigest()


md5    = Hasher(hashlib.md5)
sha1   = Hasher(hashlib.sha1)
sha224 = Hasher(hashlib.sha224)
sha256 = Hasher(hashlib.sha256)
sha384 = Hasher(hashlib.sha384)
sha512 = Hasher(hashlib.sha512)

This implementation allows me to use the functions in a similar fashion to the hashlib.foo functions:

from filehash import sha1
print sha1('somefile.txt')

Of course I could have implemented it a different way, but in this case it seemed like a simple approach.


回答 3

__call__还用于在python中实现装饰器类。在这种情况下,当调用带有装饰器的方法时,将调用类的实例。

class EnterExitParam(object):

    def __init__(self, p1):
        self.p1 = p1

    def __call__(self, f):
        def new_f():
            print("Entering", f.__name__)
            print("p1=", self.p1)
            f()
            print("Leaving", f.__name__)
        return new_f


@EnterExitParam("foo bar")
def hello():
    print("Hello")


if __name__ == "__main__":
    hello()

__call__ is also used to implement decorator classes in python. In this case the instance of the class is called when the method with the decorator is called.

class EnterExitParam(object):

    def __init__(self, p1):
        self.p1 = p1

    def __call__(self, f):
        def new_f():
            print("Entering", f.__name__)
            print("p1=", self.p1)
            f()
            print("Leaving", f.__name__)
        return new_f


@EnterExitParam("foo bar")
def hello():
    print("Hello")


if __name__ == "__main__":
    hello()

回答 4

是的,当您知道要处理对象时,完全有可能(在很多情况下建议使用)显式方法调用。但是,有时您要处理期望可调用对象的代码-通常是函数,但是由于__call__您可以构建更复杂的对象,实例数据和更多方法来委派仍可调用的重复性任务等。

另外,有时您同时将对象用于复杂的任务(编写专用类是有意义的)和对象将用于简单任务(已存在于函数中,或者更容易编写为函数)使用。要拥有一个通用的接口,您要么必须编写用期望的接口包装这些函数的微型类,要么保留这些函数并使更复杂的对象可调用。让我们以线程为例。Thread来自标准库模块threading对象需要一个可调用的target参数(即在新线程中执行的操作)。对于可调用对象,您不仅可以使用函数,还可以传递其他对象,例如相对复杂的工作程序,该工作程序从其他线程获取要执行的任务并依次执行:

class Worker(object):
    def __init__(self, *args, **kwargs):
        self.queue = queue.Queue()
        self.args = args
        self.kwargs = kwargs

    def add_task(self, task):
        self.queue.put(task)

    def __call__(self):
        while True:
            next_action = self.queue.get()
            success = next_action(*self.args, **self.kwargs)
            if not success:
               self.add_task(next_action)

这只是我脑中浮现的一个例子,但我认为它已经足够复杂,足以保证上课。仅使用函数很难做到这一点,至少它需要返回两个函数,并且这种情况正在逐渐变得复杂。一个可以重命名__call__到别的东西,并通过绑定方法,但是这使得代码稍微不太明显的创建线程,并且不会增加任何价值。

Yes, when you know you’re dealing with objects, it’s perfectly possible (and in many cases advisable) to use an explicit method call. However, sometimes you deal with code that expects callable objects – typically functions, but thanks to __call__ you can build more complex objects, with instance data and more methods to delegate repetitive tasks, etc. that are still callable.

Also, sometimes you’re using both objects for complex tasks (where it makes sense to write a dedicated class) and objects for simple tasks (that already exist in functions, or are more easily written as functions). To have a common interface, you either have to write tiny classes wrapping those functions with the expected interface, or you keep the functions functions and make the more complex objects callable. Let’s take threads as example. The Thread objects from the standard libary module threading want a callable as target argument (i.e. as action to be done in the new thread). With a callable object, you are not restricted to functions, you can pass other objects as well, such as a relatively complex worker that gets tasks to do from other threads and executes them sequentially:

class Worker(object):
    def __init__(self, *args, **kwargs):
        self.queue = queue.Queue()
        self.args = args
        self.kwargs = kwargs

    def add_task(self, task):
        self.queue.put(task)

    def __call__(self):
        while True:
            next_action = self.queue.get()
            success = next_action(*self.args, **self.kwargs)
            if not success:
               self.add_task(next_action)

This is just an example off the top of my head, but I think it is already complex enough to warrant the class. Doing this only with functions is hard, at least it requires returning two functions and that’s slowly getting complex. One could rename __call__ to something else and pass a bound method, but that makes the code creating the thread slightly less obvious, and doesn’t add any value.


回答 5

基于类的装饰器__call__用来引用包装的函数。例如:

class Deco(object):
    def __init__(self,f):
        self.f = f
    def __call__(self, *args, **kwargs):
        print args
        print kwargs
        self.f(*args, **kwargs)

Artima.com上的各种选项都有很好的描述

Class-based decorators use __call__ to reference the wrapped function. E.g.:

class Deco(object):
    def __init__(self,f):
        self.f = f
    def __call__(self, *args, **kwargs):
        print args
        print kwargs
        self.f(*args, **kwargs)

There is a good description of the various options here at Artima.com


回答 6

IMHO __call__方法和闭包为我们提供了一种在Python中创建STRATEGY设计模式的自然方法。我们定义了一系列算法,将每个算法封装在一起,使其可互换,最后我们可以执行一组通用步骤,例如,计算文件的哈希值。

IMHO __call__ method and closures give us a natural way to create STRATEGY design pattern in Python. We define a family of algorithms, encapsulate each one, make them interchangeable and in the end we can execute a common set of steps and, for example, calculate a hash for a file.


回答 7

我只是偶然发现了一种我认为很美的__call__()音乐会__getattr__()。它允许您在对象内部隐藏JSON / HTTP /(however_serialized)API的多个级别。

__getattr__()部分负责迭代地返回相同类的修改后的实例,并一次填充一个以上的属性。然后,在用尽所有信息之后,__call__()接管您传入的所有参数。

例如,使用该模型,您可以进行调用,例如api.v2.volumes.ssd.update(size=20),最终导致对的PUT请求https://some.tld/api/v2/volumes/ssd/update

特定的代码是OpenStack中特定卷后端的块存储驱动程序,您可以在此处查看:https : //github.com/openstack/cinder/blob/master/cinder/volume/drivers/nexenta/jsonrpc.py

编辑:更新了链接以指向主修订。

I just stumbled upon a usage of __call__() in concert with __getattr__() which I think is beautiful. It allows you to hide multiple levels of a JSON/HTTP/(however_serialized) API inside an object.

The __getattr__() part takes care of iteratively returning a modified instance of the same class, filling in one more attribute at a time. Then, after all information has been exhausted, __call__() takes over with whatever arguments you passed in.

Using this model, you can for example make a call like api.v2.volumes.ssd.update(size=20), which ends up in a PUT request to https://some.tld/api/v2/volumes/ssd/update.

The particular code is a block storage driver for a certain volume backend in OpenStack, you can check it out here: https://github.com/openstack/cinder/blob/master/cinder/volume/drivers/nexenta/jsonrpc.py

EDIT: Updated the link to point to master revision.


回答 8

指定一个__metaclass__并覆盖该__call__方法,并让指定的元类的__new__方法返回该类的实例,中提琴表示您具有带有方法的“函数”。

Specify a __metaclass__ and override the __call__ method, and have the specified meta classes’ __new__ method return an instance of the class, viola you have a “function” with methods.


回答 9

我们可以使用__call__method来将其他类方法用作静态方法。

    class _Callable:
        def __init__(self, anycallable):
            self.__call__ = anycallable

    class Model:

        def get_instance(conn, table_name):

            """ do something"""

        get_instance = _Callable(get_instance)

    provs_fac = Model.get_instance(connection, "users")             

We can use __call__ method to use other class methods as static methods.

    class _Callable:
        def __init__(self, anycallable):
            self.__call__ = anycallable

    class Model:

        def get_instance(conn, table_name):

            """ do something"""

        get_instance = _Callable(get_instance)

    provs_fac = Model.get_instance(connection, "users")             

回答 10

一个常见的示例是__call__in functools.partial,这是一个简化的版本(Python> = 3.5):

class partial:
    """New function with partial application of the given arguments and keywords."""

    def __new__(cls, func, *args, **kwargs):
        if not callable(func):
            raise TypeError("the first argument must be callable")
        self = super().__new__(cls)

        self.func = func
        self.args = args
        self.kwargs = kwargs
        return self

    def __call__(self, *args, **kwargs):
        return self.func(*self.args, *args, **self.kwargs, **kwargs)

用法:

def add(x, y):
    return x + y

inc = partial(add, y=1)
print(inc(41))  # 42

One common example is the __call__ in functools.partial, here is a simplified version (with Python >= 3.5):

class partial:
    """New function with partial application of the given arguments and keywords."""

    def __new__(cls, func, *args, **kwargs):
        if not callable(func):
            raise TypeError("the first argument must be callable")
        self = super().__new__(cls)

        self.func = func
        self.args = args
        self.kwargs = kwargs
        return self

    def __call__(self, *args, **kwargs):
        return self.func(*self.args, *args, **self.kwargs, **kwargs)

Usage:

def add(x, y):
    return x + y

inc = partial(add, y=1)
print(inc(41))  # 42

回答 11

函数调用运算符。

class Foo:
    def __call__(self, a, b, c):
        # do something

x = Foo()
x(1, 2, 3)

__call__方法可用于重新定义的/重新初始化相同的对象。通过将参数传递给对象,还有助于将类的实例/对象用作函数。

The function call operator.

class Foo:
    def __call__(self, a, b, c):
        # do something

x = Foo()
x(1, 2, 3)

The __call__ method can be used to redefined/re-initialize the same object. It also facilitates the use of instances/objects of a class as functions by passing arguments to the objects.


回答 12

我发现使用可调用对象的好地方,那些定义__call__(),是使用Python中的函数式编程功能时,比如map()filter()reduce()

在普通函数或lambda函数上使用可调用对象的最佳时间是逻辑复杂且需要保留某些状态或使用其他未传递给__call__()函数的信息时。

以下是一些代码,它们使用可调用对象和,根据文件名的扩展名过滤文件名filter()

可调用:

import os

class FileAcceptor(object):
    def __init__(self, accepted_extensions):
        self.accepted_extensions = accepted_extensions

    def __call__(self, filename):
        base, ext = os.path.splitext(filename)
        return ext in self.accepted_extensions

class ImageFileAcceptor(FileAcceptor):
    def __init__(self):
        image_extensions = ('.jpg', '.jpeg', '.gif', '.bmp')
        super(ImageFileAcceptor, self).__init__(image_extensions)

用法:

filenames = [
    'me.jpg',
    'me.txt',
    'friend1.jpg',
    'friend2.bmp',
    'you.jpeg',
    'you.xml']

acceptor = ImageFileAcceptor()
image_filenames = filter(acceptor, filenames)
print image_filenames

输出:

['me.jpg', 'friend1.jpg', 'friend2.bmp', 'you.jpeg']

I find a good place to use callable objects, those that define __call__(), is when using the functional programming capabilities in Python, such as map(), filter(), reduce().

The best time to use a callable object over a plain function or a lambda function is when the logic is complex and needs to retain some state or uses other info that in not passed to the __call__() function.

Here’s some code that filters file names based upon their filename extension using a callable object and filter().

Callable:

import os

class FileAcceptor(object):
    def __init__(self, accepted_extensions):
        self.accepted_extensions = accepted_extensions

    def __call__(self, filename):
        base, ext = os.path.splitext(filename)
        return ext in self.accepted_extensions

class ImageFileAcceptor(FileAcceptor):
    def __init__(self):
        image_extensions = ('.jpg', '.jpeg', '.gif', '.bmp')
        super(ImageFileAcceptor, self).__init__(image_extensions)

Usage:

filenames = [
    'me.jpg',
    'me.txt',
    'friend1.jpg',
    'friend2.bmp',
    'you.jpeg',
    'you.xml']

acceptor = ImageFileAcceptor()
image_filenames = filter(acceptor, filenames)
print image_filenames

Output:

['me.jpg', 'friend1.jpg', 'friend2.bmp', 'you.jpeg']

回答 13

现在为时已晚,但我举一个例子。假设您有一Vector堂课和一Point堂课。两者都x, y作为位置参数。假设您要创建一个函数来移动要放在矢量上的点。

4个解决方案

  • put_point_on_vec(point, vec)

  • 使其成为vector类的方法。例如 my_vec.put_point(point)

  • 使它成为Point该类的一个方法。my_point.put_on_vec(vec)
  • Vector工具__call__,因此您可以像my_vec_instance(point)

这实际上是我正在研究的一些示例的一部分,该指南针对用数学解释的Dunder方法指南(我迟早要发布)。

我离开了移动点本身的逻辑,因为这不是这个问题的目的

This is too late but I’m giving an example. Imagine you have a Vector class and a Point class. Both take x, y as positional args. Let’s imagine you want to create a function that moves the point to be put on the vector.

4 Solutions

  • put_point_on_vec(point, vec)

  • Make it a method on the vector class. e.g my_vec.put_point(point)

  • Make it a method on the Point class. my_point.put_on_vec(vec)
  • Vector implements __call__, So you can use it like my_vec_instance(point)

This is actually part of some examples I’m working on for a guide for dunder methods explained with Maths that I’m gonna release sooner or later.

I left the logic of moving the point itself because this is not what this question is about


__str__和__repr__之间的区别?

问题:__str__和__repr__之间的区别?

__str____repr__Python 和有什么不一样?

What is the difference between __str__ and __repr__ in Python?


回答 0

亚历克斯总结得很好,但令人惊讶的是,它太简洁了。

首先,让我重申亚历克斯(Alex)帖子中的要点:

  • 默认的实现是无用的(很难想到不会的,但是是的)
  • __repr__ 目标是明确
  • __str__ 目标是可读
  • 容器__str__使用的包含对象__repr__

默认实现是没有用的

这主要是令人惊讶的,因为Python的默认设置往往非常有用。但是,在这种情况下,具有默认值的__repr__行为如下:

return "%s(%r)" % (self.__class__, self.__dict__)

太危险了(例如,如果对象之间互相引用,则很容易陷入无限递归)。因此,Python应对了。请注意,有一个默认值为true:如果__repr__已定义,但未定义,__str__则该对象的行为就好像__str__=__repr__

简单来说,这意味着:您实现的几乎每个对象都应具有__repr__可用于理解该对象的功能。实施__str__是可选的:如果您需要“漂亮的打印”功能(例如,由报告生成器使用),请执行此操作。

的目标__repr__是明确的

我马上说出来-我不相信调试器。我真的不知道如何使用任何调试器,也从未认真使用过。此外,我相信调试器的最大缺陷是它们的基本特性–我调试的大多数故障是很久以前发生的,它位于一个遥远的星系中。这意味着我确实以宗教的热情相信伐木。日志记录是任何体面的“一劳永逸”服务器系统的命脉。使用Python可以轻松记录日志:也许有一些特定于项目的包装器,您只需要一个

log(INFO, "I am in the weird function and a is", a, "and b is", b, "but I got a null C — using default", default_c)

但是,您必须做最后一步-确保实现的每个对象都有一个有用的代表,这样的代码才能正常工作。这就是为什么出现“评估”问题的原因:如果您有足够的信息eval(repr(c))==c,这意味着您知道所有要了解的信息c。如果那很容易(至少以一种模糊的方式),那就去做。如果没有,请确保您有足够的信息c。我通常使用类似eval的格式:"MyClass(this=%r,that=%r)" % (self.this,self.that)。这并不意味着您可以实际构造MyClass,也不意味着它们是正确的构造方法参数—但这是表达“这是您需要了解的有关该实例的一切”的有用形式。

注意:我在%r上面使用过,不是%s。您总是想在实现中使用repr()[或%r等效地格式化字符] __repr__,否则您就无法实现repr的目标。你要能够区分MyClass(3)MyClass("3")

的目标__str__是可读

具体来说,它并非旨在明确-请注意str(3)==str("3")。同样,如果实现IP抽象,则让其str看起来像192.168.1.1很好。在实现日期/时间抽象时,str可以是“ 2010/4/12 15:35:22”,等等。目标是以用户(而不是程序员)想要阅读的方式表示它。砍掉无用的数字,冒充其他类别-只要它支持可读性,它就是一种进步。

容器__str__使用的包含对象__repr__

这似乎令人惊讶,不是吗?它有点,但是如果使用它们,它们的可读性如何__str__

[moshe is, 3, hello
world, this is a list, oh I don't know, containing just 4 elements]

不是特别的。具体而言,容器中的字符串会发现太容易打乱其字符串表示形式。记住,面对歧义,Python抵制了猜测的诱惑。如果您在打印列表时需要上述行为,则只需

print "[" + ", ".join(l) + "]"

(您可能还可以弄清楚如何处理字典。

摘要

实现__repr__你实现任何类。这应该是第二天性。__str__如果您认为有一个字符串版本会影响可读性会很有用,请实施。

Alex summarized well but, surprisingly, was too succinct.

First, let me reiterate the main points in Alex’s post:

  • The default implementation is useless (it’s hard to think of one which wouldn’t be, but yeah)
  • __repr__ goal is to be unambiguous
  • __str__ goal is to be readable
  • Container’s __str__ uses contained objects’ __repr__

Default implementation is useless

This is mostly a surprise because Python’s defaults tend to be fairly useful. However, in this case, having a default for __repr__ which would act like:

return "%s(%r)" % (self.__class__, self.__dict__)

would have been too dangerous (for example, too easy to get into infinite recursion if objects reference each other). So Python cops out. Note that there is one default which is true: if __repr__ is defined, and __str__ is not, the object will behave as though __str__=__repr__.

This means, in simple terms: almost every object you implement should have a functional __repr__ that’s usable for understanding the object. Implementing __str__ is optional: do that if you need a “pretty print” functionality (for example, used by a report generator).

The goal of __repr__ is to be unambiguous

Let me come right out and say it — I do not believe in debuggers. I don’t really know how to use any debugger, and have never used one seriously. Furthermore, I believe that the big fault in debuggers is their basic nature — most failures I debug happened a long long time ago, in a galaxy far far away. This means that I do believe, with religious fervor, in logging. Logging is the lifeblood of any decent fire-and-forget server system. Python makes it easy to log: with maybe some project specific wrappers, all you need is a

log(INFO, "I am in the weird function and a is", a, "and b is", b, "but I got a null C — using default", default_c)

But you have to do the last step — make sure every object you implement has a useful repr, so code like that can just work. This is why the “eval” thing comes up: if you have enough information so eval(repr(c))==c, that means you know everything there is to know about c. If that’s easy enough, at least in a fuzzy way, do it. If not, make sure you have enough information about c anyway. I usually use an eval-like format: "MyClass(this=%r,that=%r)" % (self.this,self.that). It does not mean that you can actually construct MyClass, or that those are the right constructor arguments — but it is a useful form to express “this is everything you need to know about this instance”.

Note: I used %r above, not %s. You always want to use repr() [or %r formatting character, equivalently] inside __repr__ implementation, or you’re defeating the goal of repr. You want to be able to differentiate MyClass(3) and MyClass("3").

The goal of __str__ is to be readable

Specifically, it is not intended to be unambiguous — notice that str(3)==str("3"). Likewise, if you implement an IP abstraction, having the str of it look like 192.168.1.1 is just fine. When implementing a date/time abstraction, the str can be “2010/4/12 15:35:22”, etc. The goal is to represent it in a way that a user, not a programmer, would want to read it. Chop off useless digits, pretend to be some other class — as long is it supports readability, it is an improvement.

Container’s __str__ uses contained objects’ __repr__

This seems surprising, doesn’t it? It is a little, but how readable would it be if it used their __str__?

[moshe is, 3, hello
world, this is a list, oh I don't know, containing just 4 elements]

Not very. Specifically, the strings in a container would find it way too easy to disturb its string representation. In the face of ambiguity, remember, Python resists the temptation to guess. If you want the above behavior when you’re printing a list, just

print "[" + ", ".join(l) + "]"

(you can probably also figure out what to do about dictionaries.

Summary

Implement __repr__ for any class you implement. This should be second nature. Implement __str__ if you think it would be useful to have a string version which errs on the side of readability.


回答 1

我的经验法则: __repr__针对开发人员,__str__针对客户。

My rule of thumb: __repr__ is for developers, __str__ is for customers.


回答 2

除非您特别采取行动以确保其他情况,否则大多数类在以下两个方面均不会产生有用的结果:

>>> class Sic(object): pass
... 
>>> print str(Sic())
<__main__.Sic object at 0x8b7d0>
>>> print repr(Sic())
<__main__.Sic object at 0x8b7d0>
>>> 

如您所见-没有区别,没有信息超出类和对象的id。如果仅覆盖两个…之一:

>>> class Sic(object): 
...   def __repr__(object): return 'foo'
... 
>>> print str(Sic())
foo
>>> print repr(Sic())
foo
>>> class Sic(object):
...   def __str__(object): return 'foo'
... 
>>> print str(Sic())
foo
>>> print repr(Sic())
<__main__.Sic object at 0x2617f0>
>>> 

如您所见,如果您覆盖__repr__,这也用于__str__,但反之则不。

要知道的其他关键要点:__str__在内置容器上__repr__,对包含的项目使用,而不是对__str__。而且,尽管在典型文档中找到了有关主题的字眼,但几乎没有人为使__repr__对象的字符串成为eval可用于构建相等对象的字符串而烦恼(这太难了,而且不知道相关模块的实际导入方式会使它实际上完全不可能)。

因此,我的建议是:着重于使__str__合理的人类可读性,并__repr__尽可能地做到模棱两可,即使这会干扰使__repr__的返回值可以接受为__eval__!的模糊不可实现的目标。

Unless you specifically act to ensure otherwise, most classes don’t have helpful results for either:

>>> class Sic(object): pass
... 
>>> print str(Sic())
<__main__.Sic object at 0x8b7d0>
>>> print repr(Sic())
<__main__.Sic object at 0x8b7d0>
>>> 

As you see — no difference, and no info beyond the class and object’s id. If you only override one of the two…:

>>> class Sic(object): 
...   def __repr__(object): return 'foo'
... 
>>> print str(Sic())
foo
>>> print repr(Sic())
foo
>>> class Sic(object):
...   def __str__(object): return 'foo'
... 
>>> print str(Sic())
foo
>>> print repr(Sic())
<__main__.Sic object at 0x2617f0>
>>> 

as you see, if you override __repr__, that’s ALSO used for __str__, but not vice versa.

Other crucial tidbits to know: __str__ on a built-on container uses the __repr__, NOT the __str__, for the items it contains. And, despite the words on the subject found in typical docs, hardly anybody bothers making the __repr__ of objects be a string that eval may use to build an equal object (it’s just too hard, AND not knowing how the relevant module was actually imported makes it actually flat out impossible).

So, my advice: focus on making __str__ reasonably human-readable, and __repr__ as unambiguous as you possibly can, even if that interferes with the fuzzy unattainable goal of making __repr__‘s returned value acceptable as input to __eval__!


回答 3

__repr__:python对象的表示形式,通常eval会将其转换回该对象

__str__:是您认为的文本形式的对象

例如

>>> s="""w'o"w"""
>>> repr(s)
'\'w\\\'o"w\''
>>> str(s)
'w\'o"w'
>>> eval(str(s))==s
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    w'o"w
       ^
SyntaxError: EOL while scanning single-quoted string
>>> eval(repr(s))==s
True

__repr__: representation of python object usually eval will convert it back to that object

__str__: is whatever you think is that object in text form

e.g.

>>> s="""w'o"w"""
>>> repr(s)
'\'w\\\'o"w\''
>>> str(s)
'w\'o"w'
>>> eval(str(s))==s
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    w'o"w
       ^
SyntaxError: EOL while scanning single-quoted string
>>> eval(repr(s))==s
True

回答 4

简而言之,的目标__repr__是明确且__str__可读。

这是一个很好的例子:

>>> import datetime
>>> today = datetime.datetime.now()
>>> str(today)
'2012-03-14 09:21:58.130922'
>>> repr(today)
'datetime.datetime(2012, 3, 14, 9, 21, 58, 130922)'

阅读此文档以获取代表:

repr(object)

返回包含对象的可打印表示形式的字符串。这与转化产生的值相同(反引号)。能够以常规功能访问此操作有时很有用。对于许多类型,此函数会尝试返回一个字符串,该字符串将在传递给时产生一个具有相同值的对象eval(),否则表示形式是一个用尖括号括起来的字符串,其中包含对象类型的名称以及其他信息通常包括对象的名称和地址。类可以通过定义__repr__()方法来控制此函数为其实例返回的内容。

这是str的文档:

str(object='')

返回一个字符串,其中包含对象的可很好打印的表示形式。对于字符串,这将返回字符串本身。与的区别repr(object)在于,str(object)并非总是尝试返回可接受的字符串eval();它的目标是返回可打印的字符串。如果未提供任何参数,则返回空字符串''

In short, the goal of __repr__ is to be unambiguous and __str__ is to be readable.

Here is a good example:

>>> import datetime
>>> today = datetime.datetime.now()
>>> str(today)
'2012-03-14 09:21:58.130922'
>>> repr(today)
'datetime.datetime(2012, 3, 14, 9, 21, 58, 130922)'

Read this documentation for repr:

repr(object)

Return a string containing a printable representation of an object. This is the same value yielded by conversions (reverse quotes). It is sometimes useful to be able to access this operation as an ordinary function. For many types, this function makes an attempt to return a string that would yield an object with the same value when passed to eval(), otherwise the representation is a string enclosed in angle brackets that contains the name of the type of the object together with additional information often including the name and address of the object. A class can control what this function returns for its instances by defining a __repr__() method.

Here is the documentation for str:

str(object='')

Return a string containing a nicely printable representation of an object. For strings, this returns the string itself. The difference with repr(object) is that str(object) does not always attempt to return a string that is acceptable to eval(); its goal is to return a printable string. If no argument is given, returns the empty string, ''.


回答 5

__str____repr__Python 和有什么不一样?

__str__(读为“ dunder(双下划线)字符串”)和__repr__(读为“ dunder-repper”(对于“表示形式”))都是根据对象状态返回字符串的特殊方法。

__repr__如果__str__丢失,则提供备份行为。

因此,首先应该编写一个__repr__,使您可以从返回的字符串中重新实例化一个等效的对象,例如,使用eval或通过在Python Shell中的逐字符键入字符。

在以后的任何时候,只要__str__有人认为有必要,就可以为该实例的用户可读的字符串表示形式编写一个。

__str__

如果打印对象,或将其传递给formatstr.formatstr,则如果__str__定义了方法,则将调用该方法,否则__repr__将使用该方法。

__repr__

__repr__方法由内置函数调用,repr并且是在评估返回对象的表达式时在python shell上回显的方法。

由于它为提供了备份__str__,如果您只能写一个,请从__repr__

这是关于的内置帮助repr

repr(...)
    repr(object) -> string

    Return the canonical string representation of the object.
    For most object types, eval(repr(object)) == object.

也就是说,对于大多数对象,如果键入所打印的内容repr,则应该能够创建等效对象。但这不是默认的实现。

默认实现 __repr__

默认对象__repr__是(C Python source)类似:

def __repr__(self):
    return '<{0}.{1} object at {2}>'.format(
      self.__module__, type(self).__name__, hex(id(self)))

这意味着默认情况下,您将打印对象所属的模块,类名以及其在内存中位置的十六进制表示形式,例如:

<__main__.Foo object at 0x7f80665abdd0>

这些信息不是很有用,但是无法得出如何准确地创建任何给定实例的规范表示的方法,它总比没有好,至少告诉我们如何在内存中唯一标识它。

怎么__repr__有用?

让我们看看使用Python Shell和datetime对象有多么有用。首先,我们需要导入datetime模块:

import datetime

如果datetime.now在外壳中调用,我们将看到重新创建等效的datetime对象所需的一切。这是由datetime创建的__repr__

>>> datetime.datetime.now()
datetime.datetime(2015, 1, 24, 20, 5, 36, 491180)

如果我们打印日期时间对象,则会看到一种很好的人类可读(实际上是ISO)格式。这是通过datetime的实现的__str__

>>> print(datetime.datetime.now())
2015-01-24 20:05:44.977951

重新创建丢失的对象是一件简单的事情,因为我们没有通过复制和粘贴从__repr__输出中将其分配给变量,然后进行打印,然后将其与其他对象存储在相同的人类可读输出中:

>>> the_past = datetime.datetime(2015, 1, 24, 20, 5, 36, 491180)
>>> print(the_past)
2015-01-24 20:05:36.491180

我该如何实施?

在开发过程中,如果可能的话,您将希望能够以相同状态再现对象。例如,这就是datetime对象的定义方式__repr__Python源)。由于复制此类对象所需的所有属性,它相当复杂:

def __repr__(self):
    """Convert to formal string, for repr()."""
    L = [self._year, self._month, self._day,  # These are never zero
         self._hour, self._minute, self._second, self._microsecond]
    if L[-1] == 0:
        del L[-1]
    if L[-1] == 0:
        del L[-1]
    s = "%s.%s(%s)" % (self.__class__.__module__,
                       self.__class__.__qualname__,
                       ", ".join(map(str, L)))
    if self._tzinfo is not None:
        assert s[-1:] == ")"
        s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")"
    if self._fold:
        assert s[-1:] == ")"
        s = s[:-1] + ", fold=1)"
    return s

如果希望对象具有更易理解的表示形式,则可以执行__str__下一步。这是datetime对象(Python源)的实现方式__str__,它很容易实现,因为它已经具有以ISO格式显示它的功能:

def __str__(self):
    "Convert to string, for str()."
    return self.isoformat(sep=' ')

设置__repr__ = __str__

这是对这里提出设置的另一个答案的批评__repr__ = __str__

设置__repr__ = __str__很愚蠢- __repr__是一个后备功能__str____repr__在编写a之前应先写一个供开发人员在调试中使用的a __str__

你需要一个__str__只有当你需要的对象的文本表示。

结论

__repr__为您编写的对象进行定义,以便您和其他开发人员在开发过程中使用它时可以得到一个可重现的示例。定义__str__何时需要其人类可读的字符串表示形式。

What is the difference between __str__ and __repr__ in Python?

__str__ (read as “dunder (double-underscore) string”) and __repr__ (read as “dunder-repper” (for “representation”)) are both special methods that return strings based on the state of the object.

__repr__ provides backup behavior if __str__ is missing.

So one should first write a __repr__ that allows you to reinstantiate an equivalent object from the string it returns e.g. using eval or by typing it in character-for-character in a Python shell.

At any time later, one can write a __str__ for a user-readable string representation of the instance, when one believes it to be necessary.

__str__

If you print an object, or pass it to format, str.format, or str, then if a __str__ method is defined, that method will be called, otherwise, __repr__ will be used.

__repr__

The __repr__ method is called by the builtin function repr and is what is echoed on your python shell when it evaluates an expression that returns an object.

Since it provides a backup for __str__, if you can only write one, start with __repr__

Here’s the builtin help on repr:

repr(...)
    repr(object) -> string

    Return the canonical string representation of the object.
    For most object types, eval(repr(object)) == object.

That is, for most objects, if you type in what is printed by repr, you should be able to create an equivalent object. But this is not the default implementation.

Default Implementation of __repr__

The default object __repr__ is (C Python source) something like:

def __repr__(self):
    return '<{0}.{1} object at {2}>'.format(
      self.__module__, type(self).__name__, hex(id(self)))

That means by default you’ll print the module the object is from, the class name, and the hexadecimal representation of its location in memory – for example:

<__main__.Foo object at 0x7f80665abdd0>

This information isn’t very useful, but there’s no way to derive how one might accurately create a canonical representation of any given instance, and it’s better than nothing, at least telling us how we might uniquely identify it in memory.

How can __repr__ be useful?

Let’s look at how useful it can be, using the Python shell and datetime objects. First we need to import the datetime module:

import datetime

If we call datetime.now in the shell, we’ll see everything we need to recreate an equivalent datetime object. This is created by the datetime __repr__:

>>> datetime.datetime.now()
datetime.datetime(2015, 1, 24, 20, 5, 36, 491180)

If we print a datetime object, we see a nice human readable (in fact, ISO) format. This is implemented by datetime’s __str__:

>>> print(datetime.datetime.now())
2015-01-24 20:05:44.977951

It is a simple matter to recreate the object we lost because we didn’t assign it to a variable by copying and pasting from the __repr__ output, and then printing it, and we get it in the same human readable output as the other object:

>>> the_past = datetime.datetime(2015, 1, 24, 20, 5, 36, 491180)
>>> print(the_past)
2015-01-24 20:05:36.491180

How do I implement them?

As you’re developing, you’ll want to be able to reproduce objects in the same state, if possible. This, for example, is how the datetime object defines __repr__ (Python source). It is fairly complex, because of all of the attributes needed to reproduce such an object:

def __repr__(self):
    """Convert to formal string, for repr()."""
    L = [self._year, self._month, self._day,  # These are never zero
         self._hour, self._minute, self._second, self._microsecond]
    if L[-1] == 0:
        del L[-1]
    if L[-1] == 0:
        del L[-1]
    s = "%s.%s(%s)" % (self.__class__.__module__,
                       self.__class__.__qualname__,
                       ", ".join(map(str, L)))
    if self._tzinfo is not None:
        assert s[-1:] == ")"
        s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")"
    if self._fold:
        assert s[-1:] == ")"
        s = s[:-1] + ", fold=1)"
    return s

If you want your object to have a more human readable representation, you can implement __str__ next. Here’s how the datetime object (Python source) implements __str__, which it easily does because it already has a function to display it in ISO format:

def __str__(self):
    "Convert to string, for str()."
    return self.isoformat(sep=' ')

Set __repr__ = __str__?

This is a critique of another answer here that suggests setting __repr__ = __str__.

Setting __repr__ = __str__ is silly – __repr__ is a fallback for __str__ and a __repr__, written for developers usage in debugging, should be written before you write a __str__.

You need a __str__ only when you need a textual representation of the object.

Conclusion

Define __repr__ for objects you write so you and other developers have a reproducible example when using it as you develop. Define __str__ when you need a human readable string representation of it.


回答 6

在Hans Petter Langtangen 撰写的《用于计算科学Python脚本》一书的第358页中,明确指出:

  • 所述__repr__在所述物体的完整字符串表示目标;
  • __str__是返回一个字符串,不错的打印。

所以,我更喜欢将它们理解为

  • repr =复制
  • str =字符串(表示形式)

从用户的角度来看,尽管这是我在学习python时的一个误解。

在同一页面上还提供了一个很小但很好的示例,如下所示:

In [38]: str('s')
Out[38]: 's'

In [39]: repr('s')
Out[39]: "'s'"

In [40]: eval(str('s'))
Traceback (most recent call last):

  File "<ipython-input-40-abd46c0c43e7>", line 1, in <module>
    eval(str('s'))

  File "<string>", line 1, in <module>

NameError: name 's' is not defined


In [41]: eval(repr('s'))
Out[41]: 's'

On page 358 of the book Python scripting for computational science by Hans Petter Langtangen, it clearly states that

  • The __repr__ aims at a complete string representation of the object;
  • The __str__ is to return a nice string for printing.

So, I prefer to understand them as

  • repr = reproduce
  • str = string (representation)

from the user’s point of view although this is a misunderstanding I made when learning python.

A small but good example is also given on the same page as follows:

Example

In [38]: str('s')
Out[38]: 's'

In [39]: repr('s')
Out[39]: "'s'"

In [40]: eval(str('s'))
Traceback (most recent call last):

  File "<ipython-input-40-abd46c0c43e7>", line 1, in <module>
    eval(str('s'))

  File "<string>", line 1, in <module>

NameError: name 's' is not defined


In [41]: eval(repr('s'))
Out[41]: 's'

回答 7

除了给出的所有答案外,我想补充几点:

__repr__()当您在交互式python控制台上简单地写对象名称并按Enter时,将调用1)。

2)__str__()在带print语句的对象上使用时被调用。

3)如果__str__丢失,则打印并使用对象str()调用__repr__()进行任何功能。

4)__str__()容器,当被调用时将执行__repr__()其所包含元素的方法。

5)str()在内部调用__str__()可能会在没有基本情况的情况下递归,并且最大递归深度会出错。

6)__repr__()可以调用repr(),它将尝试自动避免无限递归,将表示的对象替换为...

Apart from all the answers given, I would like to add few points :-

1) __repr__() is invoked when you simply write object’s name on interactive python console and press enter.

2) __str__() is invoked when you use object with print statement.

3) In case, if __str__ is missing, then print and any function using str() invokes __repr__() of object.

4) __str__() of containers, when invoked will execute __repr__() method of its contained elements.

5) str() called within __str__() could potentially recurse without a base case, and error on maximum recursion depth.

6) __repr__() can call repr() which will attempt to avoid infinite recursion automatically, replacing an already represented object with ....


回答 8

老实说,eval(repr(obj))从未使用过。如果发现自己正在使用它,则应该停止操作,因为这样做eval很危险,而字符串是序列化对象(pickle替代使用)的效率很低的方法。

因此,我建议设置__repr__ = __str__。原因是str(list)调用repr元素(我认为这是Python 3未能解决的Python最大设计缺陷之一)。实际repr输出可能不会非常有用print [your, objects]

为了证明这一点,以我的经验,该repr函数最有用的用例是将一个字符串放入另一个字符串中(使用字符串格式)。这样,您不必担心转义引号或其他内容。但是请注意,这里没有eval发生任何事情。

In all honesty, eval(repr(obj)) is never used. If you find yourself using it, you should stop, because eval is dangerous, and strings are a very inefficient way to serialize your objects (use pickle instead).

Therefore, I would recommend setting __repr__ = __str__. The reason is that str(list) calls repr on the elements (I consider this to be one of the biggest design flaws of Python that was not addressed by Python 3). An actual repr will probably not be very helpful as the output of print [your, objects].

To qualify this, in my experience, the most useful use case of the repr function is to put a string inside another string (using string formatting). This way, you don’t have to worry about escaping quotes or anything. But note that there is no eval happening here.


回答 9

简而言之:

__str__用于显示对象的字符串表示形式,以方便他人阅读

__repr__用于显示的字符串表示对象。

假设我要创建一个Fraction类,其中分数的字符串表示形式为“(1/2)”,而对象(分数类)将表示为“分数(1,2)”

因此,我们可以创建一个简单的Fraction类:

class Fraction:
    def __init__(self, num, den):
        self.__num = num
        self.__den = den

    def __str__(self):
        return '(' + str(self.__num) + '/' + str(self.__den) + ')'

    def __repr__(self):
        return 'Fraction (' + str(self.__num) + ',' + str(self.__den) + ')'



f = Fraction(1,2)
print('I want to represent the Fraction STRING as ' + str(f)) # (1/2)
print('I want to represent the Fraction OBJECT as ', repr(f)) # Fraction (1,2)

To put it simply:

__str__ is used in to show a string representation of your object to be read easily by others.

__repr__ is used to show a string representation of the object.

Let’s say I want to create a Fraction class where the string representation of a fraction is ‘(1/2)’ and the object (Fraction class) is to be represented as ‘Fraction (1,2)’

So we can create a simple Fraction class:

class Fraction:
    def __init__(self, num, den):
        self.__num = num
        self.__den = den

    def __str__(self):
        return '(' + str(self.__num) + '/' + str(self.__den) + ')'

    def __repr__(self):
        return 'Fraction (' + str(self.__num) + ',' + str(self.__den) + ')'



f = Fraction(1,2)
print('I want to represent the Fraction STRING as ' + str(f)) # (1/2)
print('I want to represent the Fraction OBJECT as ', repr(f)) # Fraction (1,2)

回答 10

来自effbot 的(非官方)Python参考Wiki(归档副本)

__str__计算‘正规’的对象的字符串表示这不同于。__repr__在它不必须是一个有效的Python表达式:一个更方便或简洁表示可以被代替使用。

From an (An Unofficial) Python Reference Wiki (archive copy) by effbot:

__str__computes the “informal” string representation of an object. This differs from __repr__ in that it does not have to be a valid Python expression: a more convenient or concise representation may be used instead.


回答 11

str -从给定的对象创建一个新的字符串对象。

repr -返回对象的规范字符串表示形式。

区别:

str():

  • 使对象可读
  • 为最终用户生成输出

repr():

  • 需要复制对象的代码
  • 为开发人员生成输出

str – Creates a new string object from the given object.

repr – Returns the canonical string representation of the object.

The differences:

str():

  • makes object readable
  • generates output for end-user

repr():

  • needs code that reproduces object
  • generates output for developer

回答 12

其他答案中缺少的一个方面。的确,该模式通常是:

  • 目标__str__:人类可读
  • 目标__repr__:明确,可能通过机器读取eval

不幸的是,这种区别是有缺陷的,因为Python REPL和IPython都__repr__用于在REPL控制台中打印对象(请参阅PythonIPython的相关问题)。因此,以交互式控制台工作为目标的项目(例如Numpy或Pandas)已经开始忽略上述规则,__repr__而是提供了易于理解的实现方式。

One aspect that is missing in other answers. It’s true that in general the pattern is:

  • Goal of __str__: human-readable
  • Goal of __repr__: unambiguous, possibly machine-readable via eval

Unfortunately, this differentiation is flawed, because the Python REPL and also IPython use __repr__ for printing objects in a REPL console (see related questions for Python and IPython). Thus, projects which are targeted for interactive console work (e.g., Numpy or Pandas) have started to ignore above rules and provide a human-readable __repr__ implementation instead.


回答 13

摘自Fluent Python一书:

Python对象的基本要求是提供其自身的可用字符串表示形式,一种用于调试和记录,另一种用于呈现给最终用户。这就是为什么
特殊方法__repr____str__存在于数据模型中的原因。

From the book Fluent Python:

A basic requirement for a Python object is to provide usable string representations of itself, one used for debugging and logging, another for presentation to end users. That is why the
special methods __repr__ and __str__ exist in the data model.


回答 14

出色的答案已经涵盖了__str__和之间的区别__repr__,对我而言,这归结为前者甚至对于最终用户而言都是可读的,而后者对开发人员则尽可能有用。鉴于此,我发现的默认实现__repr__常常无法实现此目标,因为它忽略了对开发人员有用的信息。

出于这个原因,如果我有一个简单的方法__str__,我通常会尝试通过以下方法使两个方面都达到最佳:

def __repr__(self):
    return '{0} ({1})'.format(object.__repr__(self), str(self))

Excellent answers already cover the difference between __str__ and __repr__, which for me boils down to the former being readable even by an end user, and the latter being as useful as possible to developers. Given that, I find that the default implementation of __repr__ often fails to achieve this goal because it omits information useful to developers.

For this reason, if I have a simple enough __str__, I generally just try to get the best of both worlds with something like:

def __repr__(self):
    return '{0} ({1})'.format(object.__repr__(self), str(self))

回答 15

要记住的重要一件事是容器的__str__使用包含对象__repr__

>>> from datetime import datetime
>>> from decimal import Decimal
>>> print (Decimal('52'), datetime.now())
(Decimal('52'), datetime.datetime(2015, 11, 16, 10, 51, 26, 185000))
>>> str((Decimal('52'), datetime.now()))
"(Decimal('52'), datetime.datetime(2015, 11, 16, 10, 52, 22, 176000))"

Python主张明确性胜于可读性__str__调用tuple调用包含的对象’ __repr__,即对象的“正式”表示形式。尽管正式表示比非正式表示更难读,但它对歧义没有任何歧义,并且更强大。

One important thing to keep in mind is that container’s __str__ uses contained objects’ __repr__.

>>> from datetime import datetime
>>> from decimal import Decimal
>>> print (Decimal('52'), datetime.now())
(Decimal('52'), datetime.datetime(2015, 11, 16, 10, 51, 26, 185000))
>>> str((Decimal('52'), datetime.now()))
"(Decimal('52'), datetime.datetime(2015, 11, 16, 10, 52, 22, 176000))"

Python favors unambiguity over readability, the __str__ call of a tuple calls the contained objects’ __repr__, the “formal” representation of an object. Although the formal representation is harder to read than an informal one, it is unambiguous and more robust against bugs.


回答 16

简而言之:

class Demo:
  def __repr__(self):
    return 'repr'
  def __str__(self):
    return 'str'

demo = Demo()
print(demo) # use __str__, output 'str' to stdout

s = str(demo) # __str__ is used, return 'str'
r = repr(demo) # __repr__ is used, return 'repr'

import logging
logger = logging.getLogger(logging.INFO)
logger.info(demo) # use __str__, output 'str' to stdout

from pprint import pprint, pformat
pprint(demo) # use __repr__, output 'repr' to stdout
result = pformat(demo) # use __repr__, result is string which value is 'str'

In a nutshell:

class Demo:
  def __repr__(self):
    return 'repr'
  def __str__(self):
    return 'str'

demo = Demo()
print(demo) # use __str__, output 'str' to stdout

s = str(demo) # __str__ is used, return 'str'
r = repr(demo) # __repr__ is used, return 'repr'

import logging
logger = logging.getLogger(logging.INFO)
logger.info(demo) # use __str__, output 'str' to stdout

from pprint import pprint, pformat
pprint(demo) # use __repr__, output 'repr' to stdout
result = pformat(demo) # use __repr__, result is string which value is 'str'

回答 17

>>> print(decimal.Decimal(23) / decimal.Decimal("1.05"))
21.90476190476190476190476190
>>> decimal.Decimal(23) / decimal.Decimal("1.05")
Decimal('21.90476190476190476190476190')

原始数据print()的结果decimal.Decimal(23) / decimal.Decimal("1.05")被打印时被调用;此输出为字符串形式,可以使用来实现__str__()。如果仅输入表达式,我们将得到一个decimal.Decimal输出-该输出采用可通过表示的形式__repr__()。所有Python对象都有两种输出形式。字符串形式被设计为易于阅读。表示形式旨在产生输出,如果将其提供给Python解释器,则该输出将(如果可能)再现所表示的对象。

>>> print(decimal.Decimal(23) / decimal.Decimal("1.05"))
21.90476190476190476190476190
>>> decimal.Decimal(23) / decimal.Decimal("1.05")
Decimal('21.90476190476190476190476190')

When print() is called on the result of decimal.Decimal(23) / decimal.Decimal("1.05") the raw number is printed; this output is in string form which can be achieved with __str__(). If we simply enter the expression we get a decimal.Decimal output — this output is in representational form which can be achieved with __repr__(). All Python objects have two output forms. String form is designed to be human-readable. The representational form is designed to produce output that if fed to a Python interpreter would (when possible) reproduce the represented object.


回答 18

__str__可以通过调用在对象上调用,str(obj)并且应返回人类可读的字符串。

__repr__可以通过调用在对象上调用,repr(obj)并且应该返回内部对象(对象字段/属性)

此示例可能会有所帮助:

class C1:pass

class C2:        
    def __str__(self):
        return str(f"{self.__class__.__name__} class str ")

class C3:        
    def __repr__(self):        
         return str(f"{self.__class__.__name__} class repr")

class C4:        
    def __str__(self):
        return str(f"{self.__class__.__name__} class str ")
    def __repr__(self):        
         return str(f"{self.__class__.__name__} class repr")


ci1 = C1()    
ci2 = C2()  
ci3 = C3()  
ci4 = C4()

print(ci1)       #<__main__.C1 object at 0x0000024C44A80C18>
print(str(ci1))  #<__main__.C1 object at 0x0000024C44A80C18>
print(repr(ci1)) #<__main__.C1 object at 0x0000024C44A80C18>
print(ci2)       #C2 class str
print(str(ci2))  #C2 class str
print(repr(ci2)) #<__main__.C2 object at 0x0000024C44AE12E8>
print(ci3)       #C3 class repr
print(str(ci3))  #C3 class repr
print(repr(ci3)) #C3 class repr
print(ci4)       #C4 class str 
print(str(ci4))  #C4 class str 
print(repr(ci4)) #C4 class repr

__str__ can be invoked on an object by calling str(obj) and should return a human readable string.

__repr__ can be invoked on an object by calling repr(obj) and should return internal object (object fields/attributes)

This example may help:

class C1:pass

class C2:        
    def __str__(self):
        return str(f"{self.__class__.__name__} class str ")

class C3:        
    def __repr__(self):        
         return str(f"{self.__class__.__name__} class repr")

class C4:        
    def __str__(self):
        return str(f"{self.__class__.__name__} class str ")
    def __repr__(self):        
         return str(f"{self.__class__.__name__} class repr")


ci1 = C1()    
ci2 = C2()  
ci3 = C3()  
ci4 = C4()

print(ci1)       #<__main__.C1 object at 0x0000024C44A80C18>
print(str(ci1))  #<__main__.C1 object at 0x0000024C44A80C18>
print(repr(ci1)) #<__main__.C1 object at 0x0000024C44A80C18>
print(ci2)       #C2 class str
print(str(ci2))  #C2 class str
print(repr(ci2)) #<__main__.C2 object at 0x0000024C44AE12E8>
print(ci3)       #C3 class repr
print(str(ci3))  #C3 class repr
print(repr(ci3)) #C3 class repr
print(ci4)       #C4 class str 
print(str(ci4))  #C4 class str 
print(repr(ci4)) #C4 class repr

回答 19

理解__str____repr__直观,永久地将它们区分开。

__str__返回给定对象的字符串伪装体,以使眼睛可读
__repr__

在一个例子中看到它

In [30]: str(datetime.datetime.now())
Out[30]: '2017-12-07 15:41:14.002752'
Disguised in string form

至于 __repr__

In [32]: datetime.datetime.now()
Out[32]: datetime.datetime(2017, 12, 7, 15, 43, 27, 297769)
Presence in real body which allows to be manipulated directly.

我们可以__repr__方便地对结果进行算术运算。

In [33]: datetime.datetime.now()
Out[33]: datetime.datetime(2017, 12, 7, 15, 47, 9, 741521)
In [34]: datetime.datetime(2017, 12, 7, 15, 47, 9, 741521) - datetime.datetime(2
    ...: 017, 12, 7, 15, 43, 27, 297769)
Out[34]: datetime.timedelta(0, 222, 443752)

如果将操作应用于 __str__

In [35]: '2017-12-07 15:43:14.002752' - '2017-12-07 15:41:14.002752'
TypeError: unsupported operand type(s) for -: 'str' and 'str'

只返回错误。

另一个例子。

In [36]: str('string_body')
Out[36]: 'string_body' # in string form

In [37]: repr('real_body')
Out[37]: "'real_body'" #its real body hide inside

希望这可以帮助您建立具体的基础,以探索更多的答案。

Understand __str__ and __repr__ intuitively and permanently distinguish them at all.

__str__ return the string disguised body of a given object for readable of eyes
__repr__ return the real flesh body of a given object (return itself) for unambiguity to identify.

See it in an example

In [30]: str(datetime.datetime.now())
Out[30]: '2017-12-07 15:41:14.002752'
Disguised in string form

As to __repr__

In [32]: datetime.datetime.now()
Out[32]: datetime.datetime(2017, 12, 7, 15, 43, 27, 297769)
Presence in real body which allows to be manipulated directly.

We can do arithmetic operation on __repr__ results conveniently.

In [33]: datetime.datetime.now()
Out[33]: datetime.datetime(2017, 12, 7, 15, 47, 9, 741521)
In [34]: datetime.datetime(2017, 12, 7, 15, 47, 9, 741521) - datetime.datetime(2
    ...: 017, 12, 7, 15, 43, 27, 297769)
Out[34]: datetime.timedelta(0, 222, 443752)

if apply the operation on __str__

In [35]: '2017-12-07 15:43:14.002752' - '2017-12-07 15:41:14.002752'
TypeError: unsupported operand type(s) for -: 'str' and 'str'

Returns nothing but error.

Another example.

In [36]: str('string_body')
Out[36]: 'string_body' # in string form

In [37]: repr('real_body')
Out[37]: "'real_body'" #its real body hide inside

Hope this help you build concrete grounds to explore more answers.


回答 20

  1. __str__必须返回字符串对象,而__repr__可以返回任何python表达式。
  2. 如果__str__缺少实现,则将__repr__功能用作备用。如果__repr__缺少函数实现,则没有回退。
  3. 如果__repr__函数返回对象的字符串表示形式,则可以跳过__str__函数的实现。

资料来源:https : //www.journaldev.com/22460/python-str-repr-functions

  1. __str__ must return string object whereas __repr__ can return any python expression.
  2. If __str__ implementation is missing then __repr__ function is used as fallback. There is no fallback if __repr__ function implementation is missing.
  3. If __repr__ function is returning String representation of the object, we can skip implementation of __str__ function.

Source: https://www.journaldev.com/22460/python-str-repr-functions


回答 21

__repr__用于除printstr方法(__str__定义a时!)之外的所有地方

__repr__ is used everywhere, except by print and str methods (when a __str__is defined !)