__str__和__repr__之间的区别?


Answers:


2723

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

首先,让我重申亚历克斯(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__如果您认为有一个字符串版本会影响可读性会很有用,请实施。


153
绝对不同意您的观点,即调试不是可行的方法。对于开发,请使用调试器(和/或日志记录),对于生产,请使用日志记录。使用调试器,您可以查看发生问题时发生的所有错误。您可以看到整个图片。除非您记录了所有内容,否则您将无法获得所有信息。另外,如果您要记录所有内容,则必须遍历大量数据才能获得所需的内容。
塞缪尔

20
很好的答案(关于不使用调试器的一点除外)。我只想添加到另一个有关Python 3中strunicode的问答的链接,这可能与进行转换的人的讨论有关。
ThatAintWorking

11
调试器的plus1是没有用的,并且不值得一毛钱扩展。而是提高您的日志记录吞吐量。是的,这是一篇写得很好的文章。原来__repr__是我调试所需的东西。感谢您的帮助。
personal_cloud

7
除了调试器愚蠢之外,我还是学会了%r,这还是值得一票的
Mickey Perlstein

6
关于调试器与没有调试器:不要得到这样根深蒂固的意见。在某些应用程序中,调试是不现实的,通常是在涉及实时的情况下,或者当您的代码仅在几乎没有访问权限或没有控制台的平台上远程执行时。在大多数其他情况下,停止异常进行调查或设置断点将更快,因为您不必执行数千行的日志记录(这会使磁盘混乱并使应用程序变慢)。最后,并非总是可以登录,例如在嵌入式设备上,调试器也是您的朋友。
RedGlyph

523

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


2
这是正确的,因为对于obj = uuid.uuid1(),obj .__ str __()为“ 2d7fc7f0-7706-11e9-94ae-0242ac110002”并且obj .__ repr __()为“ UUID('2d7fc7f0-7706-11e9-94ae-0242ac110002 ')”。开发人员需要(价值+起源),而客户需要价值,他们不在乎如何获得价值!
Naren Yellavula

1
这里的客户不一定是最终用户。它是对象的客户端或用户。因此,如果使用的是SDK,则SDK开发人员将使用__str__常规开发人员具有可读对象的对象。另一方面,__repr__是针对SDK开发人员本身的。
Shiplu Mokaddim

398

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

>>> 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__!的模糊不可实现的目标。


34
在单元测试中,我始终检查结果eval(repr(foo))是否等于foo。您说对了,因为我不知道该模块是如何导入的,所以它在我的测试用例之外不起作用,但这至少可以确保它在某些可预测的上下文中起作用。我认为这是评估结果__repr__是否足够明确的一种好方法。在单元测试中执行此操作还有助于确保__repr__跟随类的更改。
史蒂文·T·斯奈德,

4
我总是尝试确保eval(repr(spam)) == spam(至少在正确的上下文中)或eval(repr(spam))引发SyntaxError。这样可以避免混乱。(而这几乎为内建真实,大部分STDLIB,除了,比如,递归列表,在这里a=[]; a.append(a); print(eval(repr(a)))给你[[Ellipses]]......)当然,我不这样做,实际上使用的 eval(repr(spam)),除非在单元测试进行仔细的检查...但我有时会复制并粘贴repr(spam)到一个交互式会话。
abarnert 2014年

为什么容器(列表,元组)不使用 __str__每个元素__repr__呢?对我来说,这似乎是错误的,因为我__str__在对象中实现了可读性,当它成为列表的一部分时,我却看到了丑陋的东西__repr__
SuperGeo

只是遇到了一个与eval(repr(x))即使对于内置类型也失败的事实有关的烦人的错误:class A(str, Enum): X = 'x'会在上引发SyntaxError eval(repr(A.X))。很难过,但可以理解。顺便说一句,eval(str(A.X))实际上是有效的,但是当然只有class A在范围内时,所以它可能不是很有用。
最高

@SuperGeo其他答案包括:容器str使用元素,repr因为[1, 2, 3]!= ["1", "2, 3"]
mtraceur '19

163

__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

151

简而言之,的目标__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();它的目标是返回可打印的字符串。如果未提供任何参数,则返回空字符串''


1
可打印字符串在这里是什么意思?你能解释一下吗?
Vicrobot

115

__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__何时需要其人类可读的字符串表示形式。


难道不是某些东西type(obj).__qualname__吗?
所罗门·乌科

@SolomonUcko是的,在Python 3中似乎是这样-我一直在寻找实现此目标的源代码,当我将它们汇总在一起时,将使用这些信息来更新我的答案。
亚伦·霍尔

33

在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'

在pg。#351。
jiten

4
称其repr为复制是一种误导。最好将其视为代表。
NelsonGon

31

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

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

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

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

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

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

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


13

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

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

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


19
我认为这错了。的使用eval(repr(obj))是一项健全性测试和一条经验法则-如果此操作可以正确地重新创建原始对象,那么您将拥有一个不错的__repr__实现。并不打算您实际上以这种方式序列化对象。
jwg 2014年

7
eval不是天生的危险。不大于危险unlinkopen或写入文件。我们是否应该停止写文件,因为恶意攻击可能会使用任意文件路径将内容放入其中?如果愚蠢的人哑巴使用,一切都是危险的。愚蠢是危险的。邓宁-克鲁格效应很危险。eval只是一个功能。
Luis Masuelli '16

13

简而言之:

__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)


10

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

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

区别:

str():

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

repr():

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

8

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

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

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


7

摘自Fluent Python一书:

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


5

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

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

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

4

要记住的重要一件事是容器的__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__,即对象的“正式”表示形式。尽管正式表示比非正式表示更难读,但它对歧义没有任何歧义,并且更强大。


未定义__repr____str__)时使用!所以,你错了。
jiten

4

简而言之:

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'

4
>>> 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解释器,则该输出将(如果可能)再现所表示的对象。


4

__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

3

理解__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

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


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

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


2

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.