2.1 repr()str()方法

    对于一个对象,Python提供了两种字符串表示。它们和内建函数repr()str()print()string.format()的功能是一致的。

    • 通常,str()方法表示的对象对用户更加友好。这个方法是由对象的str方法实现的。
    • repr()方法的表示通常会更加技术化,甚至有可能是一个完整的Python表达式。文档中写道:

    对于大多数类型,这个方法会尝试给出和调用eval()一样的结果。

    这个方法是由repr()方法实现的。

    • print()函数会调用str()来生成要输出的对象。
    • 字符串的format()函数也可以使用这些方法。当我们使用{!r}或者{!s}格式时,我们实际上分别调用了repr()或者str()方法。

    下面我们先来看一下这些方法的默认实现。

    下面是一个很简单的类。

    class Card:
      insure= False
      def init( self, rank, suit ):
        self.suit= suit
        self.rank= rank
        self.hard, self.soft = self._points()
    class NumberCard( Card ):
      def _points( self ):
        return int(self.rank), int(self.rank)

    我们定义了两个简单类,每个类包含4个属性。

    下面是在命令行中使用NumberCard类的结果。

    >>> x=NumberCard( '2', '♣')
    >>>str(x)
    '<main.NumberCard object at 0x1013ea610>'
    >>>repr(x)
    '<main.NumberCard object at 0x1013ea610>'
    >>>print(x)
    <main.NumberCard object at 0x1013ea610>

    可以看到,str()repr()的默认实现并不能提供非常有用的信息。

    在以下两种情况下,我们可以考虑重写str()repr()

    • 非集合对象:一个不包括任何其他集合对象的“简单”对象,这类对象的格式化通常不会特别复杂。
    • 集合对象:一个包含集合的对象,这类对象的格式化会更为复杂。

    2.1.1 非集合对象的str()repr()

    正如我们在前面看到的,str()repr()并没有提供有用的信息,我们几乎总是需要重载它们。下面是当对象中不包括集合时我们可以使用的一种方法。这些方法是我们前面定义的Card类的方法。

      def repr( self ):
        return "{class.name}(suit={suit!r}, rank={rank!r})".
    format(
          class=self.class, self.dict)
      def str( self ):
        return "{rank}{suit}".format(
    self.dict)

    这两个方法依赖于如何将对象的内部实例变量dict传递给format()函数。这种方式对于使用slots的函数并不合适,通常来说,这些都是不可变的对象。在格式规范中使用名字可以让格式化更加可读,不过它也让格式化模板更长。以repr()为例,我们传递了dictclass作为format()函数的参数。

    格式化模板使用了两种格式化的规范。

    • {class.name}模板,有时候也被写成{class.name!s},提供了类名的简单字符串表示。
    • {suit!r}和{rank!r}模板,它们都使用了!r格式规范来给repr()方法提供属性值。

    str()为例,我们只传递了对象的dict,而内部则是隐式使用了{!s}格式规范来提供str()方法的属性值。

    2.1.2 集合中的str()repr()

    涉及集合的时候,我们需要格式化集合中的单个对象以及这些对象的整体容器。下面是一个包含str()repr()的简单集合。

    class Hand:
      def init( self, dealercard, cards ):
        self.dealercard= dealercard
        self.cards= list(cards)
      def str( self ):
        return ", ".join( map(str, self.cards) )
      def repr( self ):
        return "{class.name}({dealercard!r}, {cards_str})".
    format(
        __class
    =self.__class
    ,
        _cards_str=", ".join( map(repr, self.cards) ),
        *
    self._dict
    )

    str()方法很简单。

    1.调用map函数对集合中的每个对象使用str()方法,这会基于返回的字符串集合创建一个迭代器。

    2.用",".join()将所有对象的字符串表示连接成一个长字符串。

    repr()方法更加复杂。

    1.调用map函数对集合中的每个对象应用repr()方法,这会基于返回的结果集创建一个迭代器。

    2.使用".".join()连接所有对象的字符串表示。

    3.用class、集合字符串和dict中的不同属性创建一些关键字。我们将集合字符串命名为_card_str,这样就不会和现有的属性冲突。

    4.用"{class.name}({dealer_card!r}, {_cards_str})".format()来连接类名和之前连接的对象字符串。我们使用!r格式化来保证属性也会使用repr()来转换。

    在一些情况下,我们可以优化这个过程,让它更加简单。在格式化中使用位置参数可以在一定程度上简化模板字符串。