pypy 與 python的異同

pypy支持的擴展模塊(對應Python/Modules/中的模塊)

  • pypy支持的內建模塊:

    • __builtin__:內建模塊,包含一些常用的函數,如abs()等;
    • __pypy__:提供一個由pypy解析器提供的特殊功能模塊;
    • _ast:抽象句法樹模塊的內建模塊,一般直接使用ast模塊;
    • _codecs:注冊表與基類的編解碼器的內建模塊,一般直接使用codecs模塊;
    • _collections:容器數據類型的內建模塊,一般直接使用collections模塊;
    • _continuation
    • _ffi
    • _hashlib:安全散列與消息摘要的內建模塊,一般直接使用hashlib模塊;
    • _io:io內建模塊,一般直接使用io模塊;
    • _locale:國際化的內建模塊,一般直接使用locale模塊;
    • _lsprof:Python分析器的內建模塊,一般直接使用lsprof模塊;
    • _md5:md5的內建模塊,一般直接使用md5模塊;
    • _minimal_curses:字符顯示的終端處理curses的內建模塊,一般直接使用curses模塊,_minimal_curses僅是一個殘留,只實現了部分功能,一般使用_curses
    • _multiprocessing:基于進程的并行的multiprocessing的內建模塊,一般直接使用multiprocessing模塊;
    • _random:生成偽隨機數的內建模塊,一般直接使用random模塊;
    • _rawffi_rawffi是一個通過libffi來調用C的動態庫方法和創建C對象(數組或結構體) 的內建模塊;
    • _sha:SHA-1的內建模塊,在pypy中還有sha模塊,但是一般調用hashlib,在Ptyhon2.7中sha仍存在,但已經是Deprecated,Python3.X中sha已不再使用,一般直接使用hashlib模塊;
    • _socket:Socket的內建模塊,一般直接使用socket模塊;
    • _sre:是實現正則表達式re大部分功能的內建模塊,一般直接使用re模塊;
    • _ssl:TLS/SSL的內建模塊,一般直接使用ssl模塊;
    • _warnings:警告控制的內建模塊,一般直接使用warnings模塊;
    • _weakref:弱引用的內建模塊,一般直接使用weakref模塊;
    • _winreg:Windows注冊表訪問的內建模塊,一般直接使用winreg模塊;
    • array:高效數值數組的內建模塊;
    • binascii:二進制碼與ASCII碼間轉化的內建模塊;
    • bz2:對bzip2壓縮支持的內建模塊;
    • cStringIO:對內存進行文件操作(Read and write strings as files)的C實現的內建模塊;
    • cmath:提供數學運算的C實現的內建模塊;
    • cpyextcpyext內建模塊提供讓pypy使用CPython的擴展模塊;
    • crypt:Unix密碼驗證的內建模塊;
    • errno:標準錯誤記號的內建模塊;
    • exceptions:異常處理的內建模塊;
    • fcntl:系統調用文件鎖fcntl()和ioctl()的內建模塊;
    • gc:垃圾回收的內建模塊;
    • imp:訪問import模塊接口的內建模塊;
    • itertools:高效循環的迭代函數集合的內建模塊;
    • marshal:序列化與反序列化的內建模塊之一;
    • math:提供數學運算的內建模塊;
    • mmap:內存映射文件支持的內建模塊;
    • operator:針對函數標準操作的內建模塊;
    • parser:訪問Python解析樹的內建模塊;
    • posix:POSIX調用的內建模塊;
    • pyexpat:解析xml的內建模塊,一般直接使用xml模塊;;
    • select:提供系統內I/O多路復用的內建模塊;
    • struct:將字節解析為打包的二進制數據的內建模塊;
    • symbol:Python解析樹中的常量的內建模塊;
    • sys:系統相關的參數與函數的內建模塊;
    • termios:POSIX風格的tty控制的內建模塊;
    • thread:線程的內建模塊;
    • time:時間日期的內建模塊;
    • token:Python解析樹中的常量的內建模塊;
    • unicodedata:Unicode字符數據庫的內建模塊;
    • zipimport:從ZIP歸檔中導入的內建模塊;
    • zlib:兼容gzip壓縮的內建模塊;
  • 通過純Python重寫的模塊在 lib_pypy/ (部分使用了cffi),比如:ctypescPicklecmathdbmdatetime

垃圾回收機制(gc)的不同

pypy的垃圾回收機制實現并不是使用引用計數,所以Object并不會在不被引用的情況下立即釋放掉。最明顯的影響是,文件(socket等)將不會在離開作用范圍后立馬被關閉掉。對于寫打開的文件,這可能會導致寫入的數據在緩沖區中一段時間,是的磁盤上文本被截斷或還未寫入。還可能導致文件打開數量超過系統的限制。

如果需要調試程序中哪里沒有正確關閉文件,可以使用-X track-resources來運行程序。這樣,每當GC關閉一個文件(或者socket)將會產生一個ResourceWarning。這個警告會包含文件(或者socket)打開創建的位置,方便定位問題。

測試代碼:

import time
import gc
print("start")
i = 1
while True:
    print(i)
    i += 1
    open("/data/test.log", "rw")
    if (i % 10) == 0:
        gc.collect()

print("finished")

__del__和弱引用的影響

這個問題,會影響到__del__方法調用的準確時間,因為pypy的回收是不確定的。這同樣影響到弱引用(weak references),使得弱引用會比預期的存活時間長。這導致弱引用代理(由weakref.proxy(object[, callback])返回)的實用性降低:這使得弱引用代理在目標對象的引用失效后仍然能夠被訪問,且會在某個時刻突然失效并在下次訪問時引起ReferenceError錯誤。所有使用到弱引用代理的必須小心處理ReferenceError(或者,更好的方法是使用weakref.ref()而不是weakref.proxy())。

測試代碼:

>>>> import weakref
>>>> import gc
>>>> class A(object):
....     def __init__(self):
....         self.test_attr = 100
....         
>>>> def test_func(refrence):
....     print("callback function!")
....   
>>>> a = A()
>>>> 
>>>> x = weakref.proxy(a, test_func)
>>>> x.test_attr
100
>>>> a.test_attr
100
>>>> del a
>>>> 
>>>> x.test_attr
100
>>>> gc.collect()
callback function!
0
>>>> x
<weakproxy at 0x00007f6f19011dc0; dead>
>>>> x.test_attr
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ReferenceError: weakly referenced object no longer exists
>>>>
>>>> a = A()
>>>> x = weakref.ref(a, test_func)
>>>> print x
<weakref at 0x00007f6f190631a0; to 'A'>
>>>> b = x()
>>>> b.test_attr
100
>>>> del a
>>>> gc.collect()
0
>>>> x
<weakref at 0x00007f6f190631a0; to 'A'>
>>>> b.test_attr
100

某些情況下,由于CPython的引用計數,弱引用會在它指向的對象之前或之后立即釋放掉,如果在之后釋放掉,那么回調函數將會被調用。但在pypy類似的情況下,對象和弱引用會被認為同時釋放,回調將不會被調用。

GC的其他影響

如果一個對象有__del__方法,在pypy中,__del__被調用的次數不會多于1次。但在CPython中,__del__在對象被“復活”然后“死亡”,__del__可能會被調用多次。(此處有一段不是很懂,詳見Blog[1] [2].)

GC的差異也會間接的影響到其他方面。比如,pypy代碼中的生成器,將會比CPython中更遲的被垃圾回收。這會影響到yield關鍵字,如果yieldtry:或者with:中,這會有一個issue 736

# import gc
def g():
    try:
        yield 1
    finally:
        print "finally"

g().next()
# gc.collect()

這段代碼,在pypy中是沒有打印出finally,在CPython中是有打印的。原因是,在pypy中,finally只有在對象被垃圾回收機制回收時才會打印。如果在最后調用gc.collect(),將能夠打印出來。

使用默認的GC(minimark),內建函數id()將會像CPython中那樣工作。但使用其他GC,id()返回的數字,并不是內存地址(因為一個對象的地址可能會改變),而且太頻繁調用將會引起性能問題。

如果程序中有很長的鏈表對象,每一個中都有指向下一個的引用,并且都有__del__,這會導致pypy的GC的性能下降。但在其他情況下,pypy的GC性能普遍比CPython好。

__del__還有另外一個不同的地方,如果往一個已經存在的類中動態加入__del__,它將不會調用:

>>>> class A(object):
....     pass
....
>>>> A.__del__ = lambda self: None
__main__:1: RuntimeWarning: a __del__ method added to an existing type will not be called

在pypy中,如果你將__del__動態綁定給Python的舊式類的對象(對于CPython的新式類也是不工作的),將會得到一個RuntimeWarning。修改這個issuse的方法是,在類中定義__del__,僅包含pass(或者其他實現),再在運行時動態修改;

CPython會在程序結束的時候去自動執行gc.collect(),但pypy并不會。

內建類型(types)的子類

官方實現上,CPython對于內建類型的子類的重載方法是否會被隱式調用沒有確定任何規則。類似的,這些被重載的方法不會被同一個對象的其他內置方法調用。例如:一個在dict子類中被重載的__getitem__()將不會被其內置函數get()調用。

上述的情況在CPython中和PyPy中都是正確的。不同的是,一個除self以外的另一對象的內置方法是否會調用一個被重載的方法。這通常在CPython中是不會被調用的,而在PyPy中則會被調用。例子:

class D(dict):
    def __getitem__(self, key):
        return "%r from D" % (key,)

class A(object):
    pass

a = A()
a.__dict__ = D()
a.foo = "a's own foo"
print a.foo
# CPython => a's own foo
# PyPy => 'foo' from D

glob = D(foo="base item")
loc = {}
exec "print foo" in glob, loc
# CPython => base item
# PyPy => 'foo' from D

在字典(dictionary)中作為key的自定義類的對象

class X(object):
    pass

def __evil_eq__(self, other):
    print 'hello world'
    return False

def evil(y):
    d = {X(): 1}
    X.__eq__ = __evil_eq__
    d[y] # might trigger a call to __eq__?

在CPython中,__evil_eq__可能會被調用到,盡管沒有辦法寫出一個必能重現的例子。這會發生在y is not xhash(y) == hash(x),同時,hash(x)是在x插入在一個字典中的時候計算的。如果條件滿足,這個__evil_eq__方法將會被調用。

PyPy使用一個特殊的策略來優化,一個用戶自定義類的實例作為Keys在字典當中,且這個自定義類是沒有重載__hash____eq____cmp__:當使用這個策略的時候,__eq____cmp__將不會被調用,而是通過查找id(identity)。所以在上述代碼的情況下,PyPy將能保證__eq__不會被調用。

在其他情況下(比如,有一個自定義的__hash____eq__y中),PyPy將會和CPython一樣。

原始數據類型的對象標識,isid

原始數據類型的對象標識是根據值來判斷想等,而不是包裝器的標識。這意味著對于一個任意值的整數(interger)x來說x + 1 is x + 1總是返回True。這個規則適合如下的數據類型:

  • int
  • float
  • long
  • complex
  • str(只對空字符串或單個字符的字符串有效)
  • unicode(只對空字符串或單個字符的字符串有效)
  • tuple(只對空元組有效)
  • frozenset(只對空的frozenset有效)

這個改變需要id()也要做出相應的改變。id()需要滿足如下情況:x is y <=> id(x) <=> id(y)。因此上述類型的id()將返回一個從參數計算而得到的值,因此可以大于sys.maxint(所以可以是任意長)。

記住,一個長度大于等于2的字符串,可以相等==,equal)卻不一定相同is,identical)。類似的,盡管x包含一個元組且x == (2,),但x is (2,)不一定返回True。這個規則只適應于上述的類型。strunicodetuplefrozenset是在PyPy5.4版本中才適應這個規則。在5.4之前,盡管x等于?(),但是if x is "?" 或者 if x is ()將會返回False。這個5.4中的新行為是更為接近CPython,Cpython可以精確的緩存空tuplefirzenset和長度小于等于1的字符串或者unicodes(對于字符串和unicodes并不總是會緩存,當大部分情況是)。

對于float,“is”是針對一個對象的float“位模式”。所以float('nan') is float('nan')在PyPy上返回True,在CPython上返回False,因為這是兩個對象。但是0.0 is -0.0都返回False,因為“位模式”不同。一般的,float('nan') == float('nan')總是返回False。當在容器中使用時(在列表項或者集合中),判斷相等采用if x is y or x == y(不論在Cpython中或PyPy中)。因此,因為所有的nans在PyPy中是相同的,所以不能在一個集合中使用多次,不像CPython(Issuse #1974)

其他

  • hash()方法的隨機bug在pypy中是忽略的,所以會有以下不同:
Python 3.4.0 (default, Apr 11 2014, 13:05:11) 
[GCC 4.8.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> a = "1"
 >>>hash(a)
-7159617557763069555
 >>>exit()

再次運行:

Python 3.4.0 (default, Apr 11 2014, 13:05:11) 
[GCC 4.8.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> a = "1"
>>> hash(a)
461546025534110251
>>> exit()

兩次的hash值是不同的,而在pypy中,兩次的hash值是一樣的:

Python 2.7.3 (2.2.1+dfsg-1ubuntu0.3, Sep 30 2015, 15:18:40)
[PyPy 2.2.1 with GCC 4.8.4] on linux2
Type "help", "copyright", "credits" or "license" for more information.
And now for something completely different: ``pypy is more stable than debian''
>>>> a = "1"
>>>> hash(a)
6272018864 
>>>> exit()
Python 2.7.3 (2.2.1+dfsg-1ubuntu0.3, Sep 30 2015, 15:18:40)
[PyPy 2.2.1 with GCC 4.8.4] on linux2
Type "help", "copyright", "credits" or "license" for more information.
And now for something completely different: ``we still have to write software
with a metaspace bubble in it''
>>>> a = "1"
>>>> hash(a)
6272018864
>>>> exit()
  • 不能在一個類型對象中存儲一個非字符串的key。例子:
class A(object):
    locals()[42] = 3

# 在PyPy中是不行的
  • sys.setrecursionlimit(n)僅僅是設置一個大概的值。這個通過設置一個n * 768bytes的可用棧空間來實現的。在Linux上,這個依賴于編譯器的設置,默認值768KB大約能滿足1400次調用。

  • 因為對于字典的實現是不同的,每次調用__hash____eq__所獲的準確數字并不相同。由于CPython也沒有一個準確的保證,所以不要依賴它。

  • 對于__class__的賦值使用,是基于CPython2.5上的。對于CPython2.6和CPython2.7多個特性,PyPy仍未支持(如果需要的話,是可以支持的,但在PyPy上需要比CPython2.6/2.7注意更多情況?)。

  • __builtins__的不同:
    在python中有一個內建模塊,中有一些常用函數。而該模塊在Python啟動后、且沒有執行程序員所寫的任何代碼前,Python會首先加載該內建函數到內存。

    在pypy和python2.X中,該內建模塊是__builtin__,在Python3.X中,該內建模塊是builtins

    __builtins__則是對于這個內建模塊的引用。

    無論任何地方要想使用內建模塊,都必須在該位置所處的作用域中導入__builtin__(builtins)內建模塊;而對于__builtins__卻不用導入,它在任何模塊都直接可見。

    在Python2/3中的主模塊__mian__中,__builtins__是對內建模塊__builtin__(builtins)本身的引用,即__builtins__完全等價于__builtin__(builtins)。

    在Python2/3中的主模塊__mian__中,__builtins__是對內建模塊__builtin__.__dict__(builtins.__dict__)的引用,此時__builtins__的類型是字典。

  • 對于使用無效參數直接調用內建類型的內部“魔法”函數,結果會有略微的不同。比如:[].__add__(None)(2).__add__(None)在PyPy上都將返回NotImplemented;但在CPython 中,只有(2).__add__(None)會返回NotImplemented[].__add__(None)將引起TypeError。(當然,[] + None2 + None無論在PyPy和CPython中都會引起TypeError)。這是內部實現引起的差異,因為PyPy沒有內部的C語言層面的實現(Slots?)。

  • 在CPython中,[].__add__是一個method-wrapper,而list.__add__是一個slot wrapper。在PyPy中,它們都是一些普通的綁定或未綁定的方法對象。這有時會使得一些檢查內置類型的工具混淆。比如,在標準庫inspect模塊中有一個ismethod()方法,當參數是綁定或未綁定的方法對象,這個方法將會返回True,當參數是method-wrapperslot wrapper時,方法返回False。但PyPy并不能區分它們,所以ismethod([].__add__) == ismethod(list.__add__) == True

  • 在CPython,內置類型具有各種方式實現的屬性。根據方式,如果試圖寫入(或刪除)一個只讀(或不可刪除)屬性時,將會引起一個TypeError或者AttributeError。PyPy試圖在一致性和兼容性之間取舍。這意味著一些小地方將不會和CPython引起相同的異常,比如:del (lambda:None).__closure__

  • 在純Python中,如果寫一個類class A(object): def f(self): pass,且這個類有一個沒有重載f()的子類B,這樣B.f(x)仍然會檢查xB的一個實例。在CPython中,如果一個類型是用C語言實現的,那個這個類型將會有不同的規則。如果A是采用C語言實現,所有A的實例將會被B.f(x)接受(實際上,在這個情況下B.f is A.f是成立的)。一些代碼可以在CPython上運行,但在PyPy上不能:datetime.datetime.strftime(datetime.date.today(), ...)datetime.datedatetime.datetime的父類)。無論如何,正確修復這個方法調用應該:datetime.date.today().strftime(...)

  • 在PyPy中,新式類中的__dict__屬性將會返回一個普通的dict,而不是像CPython中那樣返回一個dict的代理。改變dict將會改變類型,反之亦然。對于內置對象,PyPy將會返回一個不可修改的dict(但行為和普通的dict類似)。

  • gc模塊的一些方法和屬性會和CPython中有一些略微的不同,比如:gc.enablegc.disable有被支持,但并不是啟用或禁用GC,而是啟用或禁用終結器(finalizers)的執行。

  • 過去的版本中,PyPy在交互模式中啟動時打印一段來之#pypy IRC的隨機一行。但在release版本中,這個行為已經被抑制了。但設置一個PYPY_IRC_TOPIC環境變量將會啟動這個行為。要注意使用到的軟件包完全禁用這個功能。

  • PyPy的readline模塊是完全重寫的,它并不是GUN的readline。它添加了多行的支持(詳見multiline_input())。但另一方面parse_and_bind()的調用會被忽略(issue #2072)。

  • sys.getsizeof()總會引起TypeError。這是因為此函數的內存分析器很有可能給出與PyPy實際上不同的結果。讓sys.getsizeof()返回一個數字(需要足夠的工作)是可行的,但是這個數字有可能不能準確代表著這個對象使用的內存大小。在于系統其余部分(?)隔絕的情況下,這個數字甚至反應不了一個對象使用的內存大小。比如一個實例有maps,這個maps會經常在許多實例之間共享。在這種情況下,這個maps會被sys.getsizeof()的實現忽略,但在某些情況下,如果這個maps有著很多唯一的實例,那么它的內存開銷是很重要的。相反的,即使是不同的對象,相等的字符串可以共享它們的內部數據,或者一些空的container會一直共享它們部分的內存,直到它們不再為空。更奇怪的是,一些list會在你對它進行讀操作時創建一些對象;如果嘗試估計range(10**6)的內存大小綜合,這個操作自身會創建一百萬個整型對象,但這些對象在開始時不存在的。在CPython中也會有類似的問題,只是少一些。以上就是不實現sys.getsizeof()的原因。

  • timeit模塊在PyPy中的表現會有所不同:它但打印的時平均時間和標準差,而不是CPython中的最小值,因為最小值通常是有誤差的。

  • sysconfig模塊中的get_config_vars方法和distutil.sysconfig這兩個是未完成的。在POSIX平臺,CPython在Makefile中獲取配置變量來編譯生成解析器。PyPy也需要在編譯的期間生成這些值,但還未完成。

  • "%d" % d"%x" % x等類似的結構中,當x是一個long的子類的實例時,并且這個子類重載了__str____hex____oct__這幾個特殊方法時:PyPy將不會調用這些特殊方法;CPython是會的,但僅限于long的子類,不包括int。CPython的這些表現是很混亂的:比如,對于%x應該調用__hex__()__hex__()的功能是支持返回一個類似-0x123L的字符串了;然后%x會去掉0x和最后的L,保留其余部分。如果重載的__hex__()返回一個格式不正確的字符串,那么將會獲得一個異常(在CPython2.7.13之前會之前crash)。

  • 在CPython2.7.13中,sqlite模塊有一個更新,當有一個提交時它將不再重置所有的游標(cursors)。這會使得PyPy中有一些麻煩(CPython也可能會有),所以PyPy并沒有移植這個更改:http://bugs.python.org/issue29006

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容

  • 1.元類 1.1.1類也是對象 在大多數編程語言中,類就是一組用來描述如何生成一個對象的代碼段。在Python中這...
    TENG書閱讀 1,284評論 0 3
  • 本節內容 Python介紹 發展史 Python 2 or 3? 安裝 Hello World程序 變量 用戶輸入...
    小小不懂11閱讀 3,437評論 2 30
  • 文/Bruce.Liu1 1.Python前世今生 1.1.Python歷史 Python的創始人: Guido ...
    BruceLiu1閱讀 11,852評論 3 119
  • 連載十二 出乎意料 1. 素碧此刻才發覺胡敦厚是一本正經的,終于說了點人話,其余的時候他都是一個不折不扣的痞子貨。...
    周琦橞閱讀 363評論 8 7
  • 25歲時,我放棄原有的3線小城市工作,來到上海。不能說是為了夢想,只能說是為了實現自我價值。工作內容類似于售后客服...
    優的世界閱讀 356評論 0 0