[TOC]
Week by week
In week one the video lectures and activities from the Runestone textbook will cover the basics of classes and instances, which combine functions and data into a clear object structure. By the end of the week, you will have learned how to create custom classes and instances, and work them into a larger codebase.
在第一周瞻佛,Runestone教科書(shū)中的視頻講座和活動(dòng)將涵蓋類和實(shí)例的基礎(chǔ)知識(shí),這些知識(shí)將函數(shù)和數(shù)據(jù)組合成清晰的對(duì)象結(jié)構(gòu)裕循。 到本周末椎扬,您將學(xué)習(xí)如何創(chuàng)建自定義類和實(shí)例,以及如何將它們用于更大的代碼庫(kù)谓晌。
In week two you will learn about class inheritance, which allows you to re-use class code more effectively. By the end of the week, you will understand how to create subclasses and superclasses. You will also learn more detail about how the Python interpreter looks up instance attributes and how to override methods and variables.
在第二周中掠拳,您將學(xué)習(xí)類繼承,這將使您更有效地重用類代碼纸肉。 到本周末溺欧,您將了解如何創(chuàng)建子類和超類喊熟。 您還將了解有關(guān)Python解釋器如何查找實(shí)例屬性以及如何覆蓋方法和變量的更多詳細(xì)信息。
In week three you will learn about exceptions and unit testing. By the end of the week, you will learn how to write test cases to be more confident that your code works as expected. You will also be able to write try/except clauses that allow you to handle runtime error and give you more control over how your code is executed.
在第三周中姐刁,您將了解異常和單元測(cè)試芥牌。 到本周結(jié)束時(shí),您將學(xué)習(xí)如何編寫(xiě)測(cè)試用例聂使,以更加確信自己的代碼可以按預(yù)期工作壁拉。 您還將能夠編寫(xiě)try / except子句,這些子句允許您處理運(yùn)行時(shí)錯(cuò)誤柏靶,并提供對(duì)代碼執(zhí)行方式的更多控制弃理。
20.1. Introduction: Classes and Objects - the Basics
20.1.1. Object-oriented programming
Python is an object-oriented programming language. That means it provides features that support object-oriented programming (OOP).
到目前為止,我們一直在編寫(xiě)的程序使用 過(guò)程編程范例(use a procedural programming paradigm)屎蜓。 在過(guò)程編程中痘昌,重點(diǎn)在于編寫(xiě)對(duì)數(shù)據(jù)(data)進(jìn)行操作的功能或過(guò)程。 在面向?qū)ο蟮木幊讨芯孀攸c(diǎn)是創(chuàng)建同時(shí)包含數(shù)據(jù)和功能的對(duì)象( the creation of objects which contain both data and functionality)辆苔。
通常,每個(gè) 對(duì)象定義 對(duì)應(yīng)于現(xiàn)實(shí)世界中的 某個(gè)對(duì)象或概念返吻,并且對(duì)該對(duì)象進(jìn)行操作的函數(shù)對(duì)應(yīng)于現(xiàn)實(shí)世界對(duì)象的交互方式姑子。
20.2. Objects Revisited
In Python, every value is actually an object. 在Python中乎婿,每個(gè)值實(shí)際上都是一個(gè)對(duì)象
無(wú)論是字典测僵,列表,還是整數(shù)谢翎,它們都是對(duì)象捍靠。 程序通過(guò)與對(duì)象執(zhí)行計(jì)算或要求它們執(zhí)行方法(methods)來(lái)操縱這些對(duì)象。
To be more specific, we say that an object has a state and a collection of methods that it can perform.
一個(gè)對(duì)象的狀態(tài)(state)代表了該對(duì)象對(duì)自身的了解森逮。狀態(tài)存儲(chǔ)在實(shí)例變量(instance variables)中榨婆。例如,正如我們?cè)?turtle object 上看到的褒侧,每只烏龜?shù)臓顟B(tài)(state)都由烏龜?shù)?lt;u>位置良风,顏色,前進(jìn)方向</u>等組成闷供。
每只海龜還具有前進(jìn)烟央,后退或右轉(zhuǎn)或左轉(zhuǎn)的能力。單個(gè)海龜?shù)牟煌幵谟谕嵩啵M管它們都是海龜(object)疑俭,但它們?cè)趩蝹€(gè)狀態(tài)屬性的具體值上是不同的(可能它們?cè)诓煌奈恢没蛴胁煌臉?biāo)題)。 they differ in the specific values of the individual state attributes
屬于同一個(gè) object婿失,區(qū)別是所具有的 實(shí)例變量的值不同
<img src="https://i.ibb.co/6vmTrwF/image.png" alt="image" border="0" style="zoom:50%;" >
20.3. User Defined Classes
我們已經(jīng)見(jiàn)過(guò)這樣的 classes: str
, int
, float
and list
钞艇。這些是由Python定義的啄寡,供我們使用。然而哩照,在許多情況下挺物,當(dāng)我們解決問(wèn)題時(shí),我們需要?jiǎng)?chuàng)建與我們?cè)噲D解決的問(wèn)題相關(guān)的數(shù)據(jù)對(duì)象飘弧。我們需要?jiǎng)?chuàng)建自己的類姻乓。
例如,考慮數(shù)學(xué)點(diǎn)的概念眯牧。 在二維中蹋岩,點(diǎn)是兩個(gè)數(shù)字(坐標(biāo)),它們被一起視為一個(gè) object学少。 點(diǎn)通常用括號(hào)括起來(lái)剪个,并用逗號(hào)分隔坐標(biāo)。例如版确,
(0,0)
表示原點(diǎn)扣囊,而(x,y)
表示點(diǎn)x
向右的單位,y
單位從原點(diǎn)起向上的單位長(zhǎng)绒疗。 這(x,y)
是點(diǎn) object 的狀態(tài) state侵歇。
考慮上面的圖,我們可以繪制一個(gè)點(diǎn)對(duì)象吓蘑,如下所示惕虑。
<img src="https://i.ibb.co/SmqMmnF/image.png" alt="image" border="0" style="zoom:67%;" >
與點(diǎn)相關(guān)聯(lián)的一些典型操作可能是:求點(diǎn)的x坐標(biāo),getX磨镶,或者求點(diǎn)的y坐標(biāo)溃蔫,getY×彰ǎ可能還希望計(jì)算一個(gè)點(diǎn)到原點(diǎn)的距離伟叛,或者一個(gè)點(diǎn)到另一個(gè)點(diǎn)的距離,或者找到兩點(diǎn)之間的中點(diǎn)脐嫂,或者回答一個(gè)點(diǎn)是否落在給定的矩形或圓內(nèi)的問(wèn)題统刮。這些稱為 這個(gè)object 的 methods
<img src="https://i.ibb.co/X5211K9/image.png" alt="image" border="0" style="zoom:67%;" >
class Point: pass # 類的主體
在我 fill 這個(gè)類之前我要做的是創(chuàng)建一個(gè)實(shí)例。如果你把一個(gè)類想成一個(gè)工廠账千,實(shí)例就是工廠生產(chǎn)的東西侥蒙。方法是:
class Point: pass # 類的主體 # 創(chuàng)建這個(gè)工廠的兩個(gè)產(chǎn)品 instances: point1 = Point() point2 = Point()
盡管這看起來(lái)很像調(diào)用一個(gè)函數(shù),這實(shí)際上與 calling the function 略有不同蕊爵。因?yàn)榍懊媸穷惖拿只愿纾缓笫情_(kāi)括號(hào)和閉括號(hào),因此這是創(chuàng)建這個(gè)Point類的一個(gè)實(shí)例。
現(xiàn)在醋旦,我們可以對(duì)instance做的第一件事是恒水,我們可以設(shè)置一個(gè)instance variable。實(shí)例變量是一種存在于實(shí)例內(nèi)部的變量饲齐。如果是
point1.x = 5
钉凌,假設(shè)點(diǎn)point2.x = 10
。然后為“產(chǎn)品”:point1
和point2
各自設(shè)置了一個(gè)名為 x 的 instance variable捂人。
現(xiàn)在我們來(lái) fill 這個(gè)類:by defining what's called a method. So a method is like a function except it belongs to a class. 例如我們定義一個(gè) 方法:
getX(self)
:方法和函數(shù)的一個(gè)最重要的區(qū)別是御雕,方法總是至少有一個(gè)參數(shù)叫做self:class Point: def getX(self): # 類的主體 return self.x # 返回實(shí)例變量 x(或者理解為產(chǎn)品的x屬性) print(point1.get()) # 調(diào)用這個(gè)方法時(shí)沒(méi)有 argument
當(dāng)我們調(diào)用
point1.getX
這個(gè)方法(method)時(shí),記住方法和函數(shù)之間的區(qū)別之一就是方法屬于一個(gè)類滥搭。 因此艾猜,每當(dāng)我們調(diào)用一個(gè)方法(method)時(shí)帚桩,我們都會(huì)在特定的實(shí)際實(shí)例上調(diào)用該方法==(So whenever we call a method we have a particular actually instance that we're calling that method on.)==督笆。所以在這種情況下育叁,我們不是只調(diào)用
getX
,還要說(shuō)的是point1.getX()
愁溜。當(dāng)我們說(shuō)point1.getX()
時(shí)疾嗅,實(shí)際發(fā)生的是當(dāng)我們調(diào)用getX
時(shí),我們自動(dòng)將這個(gè)實(shí)例point1
作為方法的參數(shù) self 傳遞冕象。==(we're automatically passing in this instancepoint1
as self when we callgetX
)==因此代承,當(dāng)我們說(shuō)
point1.getx()
時(shí),即使我們?cè)谶@里沒(méi)有參數(shù)渐扮,Python所做的是论悴,它也會(huì)自動(dòng)what's before the dot or the instance調(diào)用,并把它作為self
的值傳遞給方法傳遞進(jìn)去給 argument席爽。
既然我們理解了 a
point
object 的樣子意荤,我們就可以定義一個(gè)新的類了啊片。我們希望每個(gè)點(diǎn)都有一個(gè)x和一個(gè)y屬性(attribute)只锻,所以我們的第一個(gè)類定義是這樣class Point: '''Point class for representing and manipulating x,y coordinates.''' def __init__(self): """ Create a new point at the origin """ self.x = 0 self.y = 0
如果 class header 之后的第一行是字符串,則它將成為該類的文檔字符串(docstring of the class)紫谷,并會(huì)被各種工具識(shí)別齐饮。 (文檔字符串也適用于函數(shù))
==This initializer method==: 每個(gè)類都應(yīng)該有一個(gè)特殊名稱為
__init__
的方法(通常稱為構(gòu)造函數(shù)constructor function)。每當(dāng)創(chuàng)建一個(gè)新的 Point 實(shí)例時(shí)笤昨,都會(huì)自動(dòng) call 這個(gè)初始化方法祖驱。它為程序員提供了通過(guò)給定初始狀態(tài)值 initial state values的方式 來(lái)設(shè)置新實(shí)例內(nèi)所需屬性的機(jī)會(huì) set up the attributes required within the new instance。
self
參數(shù)會(huì)自動(dòng)設(shè)置為reference需要初始化的新創(chuàng)建的對(duì)象瞒窒。
現(xiàn)在捺僻,讓我們使用新的Point類。 如果您還記得關(guān)于Turtle圖形的一章中有關(guān)如何創(chuàng)建Turtle類實(shí)例的某些語(yǔ)法,則下一部分應(yīng)該看起來(lái)有點(diǎn)熟悉匕坯。
class Point: """ Point class for representing and manipulating x,y coordinates. """ def __init__(self): self.x = 0 self.y = 0 p = Ponit() # Instantiate an object of type Point 類的實(shí)例化 q = Point() print("Nothing seems to have happened with the points")
在對(duì)象的初始化過(guò)程中束昵,我們?yōu)槊總€(gè)對(duì)象創(chuàng)建了兩個(gè)名為x和y的屬性,并給它們賦值0葛峻。你會(huì)注意到锹雏,當(dāng)你運(yùn)行程序時(shí),什么都沒(méi)有發(fā)生术奖。但實(shí)際上并非如此礁遵。實(shí)際上,已經(jīng)創(chuàng)建了兩個(gè)
Point
采记,每個(gè)Point
的x
和y
坐標(biāo)值為0
佣耐。但是,由于我們沒(méi)有要求程序?qū)@些Point
做任何事情唧龄,因此看不到其他任何結(jié)果晰赞。實(shí)際上是:<img src="https://i.ibb.co/x2mxRfz/image.png" alt="image" border="0" style="zoom: 67%;" >
A ‘function’ like
Point()
that creates a new object instance is called a constructor.用來(lái)創(chuàng)建一個(gè)新的對(duì)象的實(shí)例(產(chǎn)品)的類似
Point(<>)
的“函數(shù)”被稱作一個(gè)構(gòu)造器(constructor),而每當(dāng)調(diào)用這個(gè)“函數(shù)”創(chuàng)建了這個(gè)類的實(shí)例時(shí)就會(huì)自動(dòng)調(diào)用一個(gè)特殊的方法(method)__init__()
:被稱為類的構(gòu)造函數(shù)或初始化方法选侨。==將 class 視為制造 objects 的工廠可能會(huì)有所幫助掖鱼。 class 本身不是點(diǎn)的 instance ,但是class 包含制作點(diǎn)實(shí)例的 machinery 援制。 每次調(diào)用構(gòu)造函數(shù)
__init__()
時(shí)戏挡,您都在要求工廠為您創(chuàng)建一個(gè)新對(duì)象。 當(dāng)對(duì)象離開(kāi)生產(chǎn)線時(shí)晨仑,將執(zhí)行其初始化方法以使用其出廠默認(rèn)設(shè)置正確設(shè)置對(duì)象褐墅。(例如初始化 x=0, y =0)==<img src="https://i.ibb.co/SmqMmnF/image.png" alt="image" border="0" style="zoom:67%;" >
The combined process of “make me a new object” and “get its settings initialized to the factory default settings” is called instantiation. “給我制造一個(gè)新對(duì)象”和“將其設(shè)置初始化為出廠默認(rèn)設(shè)置”的組合過(guò)程稱為實(shí)例化。
class Point: """ Point class for representing and manipulating x,y coordinates. """ def __init__(self): self.x = 0 self.y = 0
<img src="https://i.ibb.co/j4z4ZSs/image.png" alt="image" border="0">
p = Point() # Instantiate an object of type Point q = Point() # and make a second point
The execution of
p = Point()
, occurs at steps 3-5. First, at step 3, you can see that a empty instance of the class has been created, and is passed as the first (and only parameter) to the__init__
method.當(dāng)調(diào)用了
Point()
的時(shí)候洪己,首先創(chuàng)建了這個(gè)類的 empty instance妥凳,并把這個(gè) empty instance 傳遞到__init__
方法的第一參數(shù)self【第3步】<img src="https://i.ibb.co/xgsg62q/image.png" alt="image" border="0" style="zoom: 67%;" >
執(zhí)行該方法的代碼,并將變量self綁定到該實(shí)例答捕。(深色部分)
在第4步和第5步逝钥,將對(duì)空的產(chǎn)品 設(shè)置兩個(gè)兩個(gè)(出廠的)實(shí)例變量:x和y都設(shè)置為0。
__init__
方法未返回任何內(nèi)容拱镐, but the point object itself is returned from the call toPoint()
.<img src="https://i.ibb.co/zxQSzjH/image-20200316200115514.png" alt="image-20200316200115514" border="0" style="zoom:67%;" >
thus, at step 7,
p
is bound to the new point that was created and initialized.<img src="https://i.ibb.co/j3kbQpH/image-20200316200338306.png" alt="image-20200316200338306" border="0" style="zoom:67%;" >
跳到步驟14之前艘款,p和q分別綁定到不同的Point實(shí)例。 即使x和y實(shí)例變量都設(shè)置為0沃琅,它們都是不同的對(duì)象哗咆。 因此,p是q的結(jié)果為False
20.4. Adding Parameters to the Constructor
到目前為止益眉,我們的構(gòu)造函數(shù)只能在位置(0,0)創(chuàng)建點(diǎn)晌柬。 如果我們想要在位置(7姥份,6)創(chuàng)建一個(gè)(初始)點(diǎn)。 由于構(gòu)造函數(shù)只是專門命名的“函數(shù)”年碘,因此我們可以使用參數(shù)(如前所述)來(lái)提供特定信息殿衰。
通過(guò)將額外的參數(shù)放入__init__
方法,可以使我們的類構(gòu)造函數(shù)更通用盛泡,如本示例所示闷祥。
class Point:
""" Point class for representing and manipulating x,y coordinates. """
def __init__(self, initx, inity):
# 給定任意初坐標(biāo)
self.x = initX
self.y = initY
p = Point(7,6)
當(dāng)實(shí)例(產(chǎn)品)point 被創(chuàng)建時(shí),在實(shí)例變量x和y中將initX
和initY
的值分配給對(duì)象的狀態(tài)state傲诵。
<img src="https://i.ibb.co/SQ7dWVS/image.png" alt="image" border="0" style="zoom:67%;" >
在類的__init__
方法中這是常見(jiàn)的事情:接受一些參數(shù)并將其保存為實(shí)例變量instance variables凯砍。 為什么這有用? 請(qǐng)記住拴竹,方法完成執(zhí)行后悟衩,參數(shù)變量 parameter variables 將消失。 但是栓拜,實(shí)例變量仍然可以在對(duì)象實(shí)例具有句柄的任何位置訪問(wèn)座泳。 這是保存那些在調(diào)用類構(gòu)造函數(shù)時(shí)提供的初始值的方法。
稍后幕与,您將看到
__init__
方法所做的類不僅僅是將參數(shù)保存為實(shí)例變量挑势。 例如,它可能解析這些變量的內(nèi)容并對(duì)它們進(jìn)行一些計(jì)算啦鸣,然后將結(jié)果存儲(chǔ)在實(shí)例變量中潮饱。 它甚至可以建立Internet連接,下載一些內(nèi)容诫给,并將其存儲(chǔ)在實(shí)例變量中
20.5. Adding Other Methods to a Class
使用像Point這樣的class而不是像簡(jiǎn)單元組(7,6)這樣的東西的主要優(yōu)勢(shì)現(xiàn)在變得很明顯香拉。我們可以向Point類中添加一些方法,這些方法是對(duì)點(diǎn)的合理操作
創(chuàng)建一個(gè)像Point這樣的類給我們的程序和思維帶來(lái)了大量的“組織力量”中狂。我們可以將合理的操作和它們所應(yīng)用的數(shù)據(jù)類型組合在一起凫碌,并且類的每個(gè)實(shí)例都可以有自己的狀態(tài) each instance of the class can have its own state。
A method behaves like a function but it is invoked on a specific instance. (構(gòu)造器也像一個(gè)function但是前面是類名)
For example, with a list bound to variable L: list是一個(gè)類胃榕, L 是一個(gè)實(shí)例盛险,
L.append(7)
調(diào)用類內(nèi)的方法append(self, x)
,列表本身作為第一個(gè)參數(shù)勤晚,7 作為第二個(gè)參數(shù)枉层。方法使用點(diǎn)標(biāo)記法訪問(wèn)(dot notation)。這就是為什么L.append(7)
有兩個(gè)參數(shù)赐写,盡管你可能認(rèn)為它只有一個(gè):存儲(chǔ)在變量L中的列表是第一個(gè)參數(shù)值,而7是第二個(gè)參數(shù)值膜赃。
讓我們添加兩種簡(jiǎn)單的方法挺邀,讓一個(gè)點(diǎn)向我們提供有關(guān)其狀態(tài)的信息。 調(diào)用getX方法時(shí),將返回x坐標(biāo)的值端铛。
由于我們已經(jīng)知道如何編寫(xiě)返回值的函數(shù)泣矛,因此該方法的實(shí)現(xiàn)非常簡(jiǎn)單。需要注意的一點(diǎn)是禾蚕,即使 getX
方法不需要任何其他參數(shù)信息來(lái)完成它的工作您朽,但仍然有一個(gè)形式參數(shù)self。 如前所述换淆,在類中定義的哗总,對(duì)該類的對(duì)象進(jìn)行操作的所有方法都將self作為其第一個(gè)參數(shù)。 同樣倍试,這用作對(duì)對(duì)象本身的引用讯屈,而對(duì)象本身又可以訪問(wèn)對(duì)象內(nèi)部的狀態(tài)數(shù)據(jù)。
class Point:
""" Point class for representing and manipulating x,y coordinates. """
def __init__(self, initX, initY):
self.x = initX
self.y = initY
def getX(self):
return self.x
def getY(self):
return self.y
p = Point(7,6)
print(p.getX()) # 調(diào)用對(duì)象方法
print(p.getY())
---------
7
6
注意县习,getX
方法只是從對(duì)象自身返回實(shí)例變量x的值涮母。 換句話說(shuō),該方法的實(shí)現(xiàn)是轉(zhuǎn)到對(duì)象本身的狀態(tài)并獲得x的值躁愿。 同樣叛本,getY
方法看起來(lái)幾乎相同。
讓我們添加另一個(gè)方法彤钟,distanceFromOrigin炮赦,來(lái)更好地了解方法是如何工作的。除了存儲(chǔ)在實(shí)例變量中的數(shù)據(jù)(狀態(tài))之外样勃,這個(gè)方法也不需要任何額外的信息來(lái)完成它的工作吠勘。它將執(zhí)行更復(fù)雜的任務(wù)。
class Point:
def __init__(self, initX, initY):
self.x = initX
self.y = initY
def getX(self):
return self.x
def getY(self):
return self.y
def distanceFromOrigin(self):
d = (self.x ** 2 + self.y**2) ** 0.5
return d
p = Point(7,6)
print(p.distanceFromOrigin())
------
使用 class 來(lái)表示 data
cityNames = ['Detroit', 'Ann Arbor', 'Pitts', 'Mars', 'New York']
populations = [680250, 117070, 304391, 1683, 840600]
states = ['MI', 'MI', 'PA', 'PA', 'NY']
# 遍歷多個(gè)列表峡眶,把對(duì)應(yīng)的元素組成一個(gè)元組列表
city_tuples = zip(cityNames, populations, states)
-
當(dāng)我們想用 class 來(lái)表示這些數(shù)據(jù)的時(shí)候剧防,我們首先要考慮 我們想要用 instance 存儲(chǔ)什么數(shù)據(jù)?
在這里我們假設(shè) 我們的每個(gè)實(shí)例需要存儲(chǔ)著所有三個(gè)數(shù)據(jù)
-
然后我會(huì)問(wèn)辫樱, what do I want every class to you represent? 峭拘?我想讓 class 代表什么信息?
在這種情況下狮暑,有一點(diǎn)顯而易見(jiàn)鸡挠,每個(gè)實(shí)例應(yīng)該代表一個(gè)單獨(dú)的城市,每個(gè)單獨(dú)的城市都有一個(gè)名字搬男,人口和州拣展,因此我要稱之為
city
class。
cityNames = ['Detroit', 'Ann Arbor', 'Pitts', 'Mars', 'New York']
populations = [680250, 117070, 304391, 1683, 840600]
states = ['MI', 'MI', 'PA', 'PA', 'NY']
# 遍歷多個(gè)列表缔逛,把對(duì)應(yīng)的元素組成一個(gè)元組列表
city_tuples = zip(cityNames, populations, states)
class City:
def __init__(self, n, p, s):
self.name = n
self.population = p
self.state = s
# 定義返回一個(gè) str 語(yǔ)句 包含我們想要的信息 ?
def __str__(self):
info = '{},{}(pop:{})'.format(self.name, self.state, self.population)
# 操作
cities = []
for city_tup in city_tuples:
# 復(fù)習(xí)元組數(shù)據(jù)的賦值形式
name, pop, state = city_tup
# 創(chuàng)建一個(gè)類的實(shí)例
city = City(name, pop, state)
# 打印這個(gè)類
print(city) # 圖一
cities.append(city)
print(cities) # 圖二 存疑 為什么輸出的是類型
<img src="https://i.ibb.co/9pB35tT/image-20200316222011719.png" alt="image-20200316222011719" border="0">
<img src="https://i.ibb.co/s1FNfmB/image-20200316222256704.png" alt="image-20200316222256704" border="0">
list comprehension 版本
cityNames = ['Detroit', 'Ann Arbor', 'Pitts', 'Mars', 'New York']
populations = [680250, 117070, 304391, 1683, 840600]
states = ['MI', 'MI', 'PA', 'PA', 'NY']
# 遍歷多個(gè)列表备埃,把對(duì)應(yīng)的元素組成一個(gè)元組列表
city_tuples = zip(cityNames, populations, states)
class City:
def __init__(self, n, p, s):
self.name = n
self.population = p
self.state = s
# 定義返回一個(gè) str 語(yǔ)句 包含我們想要的信息 ?
def __str__(self):
info = '{},{}(pop:{})'.format(self.name, self.state, self.population)
# list comprehension
cities = [City(n, p, s) for (n, p, s) in city_tuples if True]
print(cities)
20.6. Objects as Arguments and Parameters
您可以以通常的方式將對(duì)象作為參數(shù)傳遞給函數(shù)姓惑。
這是一個(gè)叫做距離的簡(jiǎn)單函數(shù),它涉及到我們新的點(diǎn)對(duì)象按脚。這個(gè)函數(shù)的作用是計(jì)算兩點(diǎn)之間的距離
import math
class Point:
""" Point class for representing and manipulating x,y coordinates. """
def __init__(self, initX, initY):
self.x = initX
self.y = initY
def getX(self):
return self.x
def getY(self):
return self.Y
def distanceFromOrigin(self):
d = (self.x ** 2 + self.y ** 2) ** 0.5
return d
# 將對(duì)象作為參數(shù)傳遞給函數(shù)
def distance(ponit1, point2):
'''計(jì)算兩點(diǎn)之間的距離'''
xdiff = point2.getX() - point1.getX()
ydiff = point2.getY() - point1.getY()
# 數(shù)學(xué)公式
dist = math.sqrt(xdiff**2 + ydiff**2)
return dist
p = Point(4,3)
q = Point(0,0)
# 打印給定兩點(diǎn)的距離 dist
print(distance(p,q))
20.7. __str__
方法
當(dāng)我們使用類和對(duì)象時(shí)于毙,通常需要打印一個(gè)對(duì)象(即打印對(duì)象的狀態(tài))。 例如對(duì)于上面的例子辅搬,如果我們打印 instance 本身我們將會(huì)看到:
class Point:
""" Point class for representing and manipulating x,y coordinates. """
def __init__(self, initX, initY):
self.x = initX
self.y = initY
def getX(self):
return self.x
def getY(self):
return self.y
def distanceFromOrigin(self):
return ((self.x ** 2) + (self.y ** 2)) ** 0.5
p = Point(7,6)
print(p)
<__main__.Point object>
上面顯示的 print
函數(shù)生成點(diǎn) p
的 a string representation唯沮。 Python默認(rèn)功能告訴您 p
是 Point
類型的對(duì)象。 但是堪遂,它不會(huì)告訴您有關(guān)該點(diǎn)的特定狀態(tài)(state)的任何信息,
但如果類定義中包含一個(gè)特殊的方法調(diào)用 __str__
介蛉,我們可以改進(jìn)這個(gè)表示。請(qǐng)注意蚤氏,此方法使用與構(gòu)造函數(shù)相同的命名約定甘耿,即名稱前后有兩個(gè)下劃線。Python對(duì)特殊方法使用這種命名技術(shù)是很常見(jiàn)的竿滨。
__str__
方法的作用是告訴Python在實(shí)際打印該對(duì)象時(shí)如何表示該對(duì)象佳恬。
__str__
方法負(fù)責(zé)返回類的創(chuàng)建者所定義的一種字符串表示形式。換句話說(shuō)于游,作為程序員毁葱,你可以選擇一個(gè)Point
(instance)在被打印時(shí)應(yīng)該是什么樣子。在這個(gè)例子里贰剥,我們決定__str__
方法需要?jiǎng)?chuàng)建并返回一個(gè)字符串倾剿,字符串表示 將包括 x
和 y
的值以及一些識(shí)別文本。
類的
__str__
方法 return 的字符串是當(dāng)我們 print 該類的任何 instance 時(shí)所打印的字符串蚌成。因此前痘,類的__str__
方法返回的字符串通常應(yīng)該包含instance variables的值。如果一個(gè)點(diǎn)的x值為3担忧,y值為4芹缔,而另一個(gè)點(diǎn)的x值為5,y值為9瓶盛,那么這兩個(gè)點(diǎn)對(duì)象在打印時(shí)應(yīng)該會(huì)有所不同最欠,對(duì)嗎?
class Point:
""" Point class for representing and manipulating x,y coordinates. """
def __init__(self, initX, initY):
self.x = initX
self.y = initY
def getX(self):
return self.x
def getY(self):
return self.y
def distanceFromOrigin(self):
return ((self.x ** 2) + (self.y ** 2)) ** 0.5
def __str__(self):
return "x = {}, y = {}".format(self.x, self.y)
# return 'Point({},{})'.format(self.x, self.y)
p = Point(7,6)
print(p)
x = 7, y = 6
#Point(7,6)
因此惩猫,在創(chuàng)建類時(shí)芝硬,您通常希望設(shè)置 __str__
方法,以便在實(shí)際打印出任何特定實(shí)例時(shí)可以打印出更具可讀性和可理解性的內(nèi)容轧房。
特殊下劃線方法
假設(shè)我們已經(jīng)有了一個(gè)類:
class Point:
""" Point class for representing and manipulating x,y coordinates. """
def __init__(self, initX, initY):
self.x = initX
self.y = initY
def __str__(self):
return 'Point({},{})'.format(self.x, self.y)
p1 = Point(-5, 10)
p2 = Point(15, 20)
print(p1)
print(p2)
# 因此拌阴,假設(shè)我們希望能夠?qū)蓚€(gè)點(diǎn)加在一起, 例如
print(p1 + p2) # 但是 python 并不知道怎么把兩個(gè) instance 相加
但是我們可以 override 覆寫(xiě) __add__
方法來(lái)告訴 python如何將這兩個(gè) instance 相加
def __add__(self, otherPoint):
self
是將自身作為第一個(gè)參數(shù)
otherPoint
是另一個(gè)實(shí)例點(diǎn)將設(shè)
print(p1 + p2)
則p1 = self, p2 = otherPoint
<img src="https://i.ibb.co/zxDYTNj/image.png" alt="image" border="0" style="zoom:70%;" >
def __add__(self, otherPoint):
# 一個(gè)新的 Point 并且在print時(shí)會(huì)調(diào)用 __str__
return Point(p1.x + otherPoint.x,
p1.y + otherPoint.y)
print(p1 + p2)
---
Point(10,20)
同理我們也可以覆寫(xiě) subtraction
def __sub__(self, otherPoint):
# 一個(gè)新的 Point 并且在print時(shí)會(huì)調(diào)用 __str__
return Point(p1.x - otherPoint.x,
p1.y - otherPoint.y)
還有許多可以被 override 的 methods 都是這種模式锯厢。
20.8. Instances as Return Values
函數(shù)和方法可以返回對(duì)象皮官。這實(shí)際上并不新鮮脯倒,因?yàn)镻ython中的所有東西都是一個(gè)對(duì)象实辑, we have been returning values for quite some time.捺氢。(您也可以返回對(duì)象實(shí)例的列表或元組等。)這里的區(qū)別在于剪撬,我們希望在類中的方法使用構(gòu)造函數(shù)創(chuàng)建一個(gè)對(duì)象摄乒,然后將其作為方法的 值 返回。
return Point(mx, my) # 方法返回一個(gè)新的對(duì)象
假設(shè)您有一個(gè) a point object残黑,并希望在它和其他目標(biāo)點(diǎn)之間找到 midpoint 馍佑。 我們想編寫(xiě)一個(gè)方法 method,將其稱為 halfway
梨水,這個(gè)Point的 method 將另一個(gè) Point對(duì)象 用作參數(shù)拭荤,并返回位于該 point 與其接受作為輸入的 target point 之間的一半作為新的 Point 實(shí)例。
class Point:
def __init__(self, initX, initY):
self.x = initX
self.y = initY
def getX(self):
return self.x
def getY(self):
return self.y
def distanceFromOrigin(self):
return ((self.x ** 2) + (self.y ** 2)) ** 0.5
def halfway(self, target):
mx = (self.x + target.x) / 2
my = (self.y + target.y) / 2
return Point(mx, my) # 方法返回一個(gè)新的對(duì)象
def __str__(self):
return "x = {}, y = {}".format(self.x, self.y)
p = Point(3,4)
q = Point(5,12)
mid = p.halfway(q) # should return a new Point that is halfway between p and q.
print(mid)
print(mid.getX())
print(mid.getY())
----
x = 4.0, y = 8.0
4.0
8.0
所得的Point實(shí)例:mid
的 x 值為4疫诽,y 值為8舅世。我們也可以對(duì) mid
使用任何其他 method,因?yàn)樗荘oint對(duì)象奇徒。
20.9. Sorting Lists of Instances
您以前學(xué)習(xí)過(guò)如何對(duì)列表進(jìn)行排序雏亚。對(duì)一個(gè)類的實(shí)例列表進(jìn)行排序與對(duì)任何其他類型的對(duì)象列表進(jìn)行排序沒(méi)有根本的區(qū)別。 您應(yīng)該只提供 a key function as a parameter to sorted 鍵函數(shù)作為要排序(或排序)的參數(shù)摩钙。
之前罢低,已經(jīng)了解了如何使用這種函數(shù) 對(duì)其他類型的對(duì)象列表進(jìn)行排序。 例如胖笛,給定一個(gè)字符串列表网持,可以通過(guò)傳遞鍵參數(shù)(a key parameter)以其長(zhǎng)度的升序 (in ascending order of their lengths)對(duì)其進(jìn)行排序。
請(qǐng)注意长踊,如果通過(guò)名稱引用函數(shù)功舀,則要在函數(shù)名稱后加上括號(hào),因?yàn)槟枰瘮?shù)對(duì)象本身之斯。而不添加括號(hào)意味著日杈,排序中的函數(shù)將負(fù)責(zé)調(diào)用該函數(shù),并傳遞列表中的當(dāng)前項(xiàng)目佑刷。 因此莉擒,在下面的示例中,我們編寫(xiě)
key = len
而不是key = len()
瘫絮。
用作鍵參數(shù)的函數(shù)的特點(diǎn)是輸入 列表中的元素返回?cái)?shù)字
L = ["Cherry", "Apple", "Blueberry"]
print(sorted(L, key=len))
#alternative form using lambda, if you find that easier to understand
print(sorted(L, key= lambda x: len(x)))
['Apple', 'Cherry', 'Blueberry']
['Apple', 'Cherry', 'Blueberry']
當(dāng)列表中的每個(gè)項(xiàng)目都是一個(gè)類的實(shí)例時(shí)涨冀,您需要提供一個(gè)將一個(gè)實(shí)例作為輸入并返回?cái)?shù)字的函數(shù)。 實(shí)例將按其編號(hào)排序麦萤。
L = [Fruit("Cherry", 10), Fruit("Apple", 5), Fruit("Blueberry", 20)]
class Fruit():
def __init__(self, name, price):
self.name = name
self.price = price
for f in sorted(L, key=lambda x: x.price):
print(f.name)
Apple
Cherry
Blueberry
有時(shí)鹿鳖,您會(huì)發(fā)現(xiàn)為類定義一個(gè)對(duì)實(shí)例中的數(shù)據(jù)進(jìn)行計(jì)算的方法很方便
但我們的類太簡(jiǎn)單了扁眯,無(wú)法真正說(shuō)明這一點(diǎn)。但是為了模擬它翅帜,我定義了一個(gè)方法 sort_priority
姻檀,它只返回存儲(chǔ)在 instance 中的 price。現(xiàn)在涝滴,方法 sort_priority
將一個(gè)instance作為輸入并返回一個(gè) number绣版。因此,它符合為排序提供的 key parameter歼疮。
Here it can get a little confusing: to refer to that method, without actually invoking it, you can refer to
Fruit.sort_priority
. This is analogous to the code above that referred tolen
rather than invokinglen()
.
class Fruit(): # ?括號(hào)
def __init__(self, name, price):
self.name = name
self.price = price
def sort_priority(self):
return self.price
L = [Fruit("Cherry", 10), Fruit("Apple", 5), Fruit("Blueberry", 20)]
print('------按照價(jià)格排序杂抽, refer 一個(gè) 類的方法')
for f in sorted(L, key=Fruit.sort_priority):
print(f.name)
print('----- 另一種方法------x 是,每一個(gè)單個(gè)的 fruit instance')
for f in sorted(L, key=lambda x: x.sort_priority())
key參數(shù)使用了類中定義的一個(gè)方法韩脏,注意返回?cái)?shù)字缩麸,實(shí)現(xiàn)key參數(shù)為lambda函數(shù)同樣的效果,
20.10. Class Variables and Instance Variables
您已經(jīng)看到赡矢,一個(gè)類的每個(gè)實(shí)例都有自己的名稱空間和自己的實(shí)例變量杭朱。Point類的兩個(gè)實(shí)例各有自己的實(shí)例變量x。在一個(gè)實(shí)例中設(shè)置x不會(huì)影響另一個(gè)實(shí)例济竹。
一個(gè)類也可以具有類變量痕檬。 類變量被設(shè)置為類定義的一部分。
例如送浊,考慮以下版本的Point類梦谜。 在這里,我們添加了一個(gè)graph方法袭景,該方法生成一個(gè)字符串唁桩,該字符串表示一個(gè)基于文本的小圖表,在該圖形上繪制了Point耸棒。 它不是一個(gè)非常漂亮的圖形荒澡,部分是因?yàn)閥軸像橡皮筋一樣被拉伸,但是您可以從中得到靈感与殃。
請(qǐng)注意单山,在第4行上有一個(gè)對(duì)printed_rep
變量的賦值。它不在任何方法中幅疼。 這使其成為類變量米奸。 它的訪問(wèn)方式與實(shí)例變量相同。 例如爽篷,在第16行悴晰,有對(duì)self.printed_rep
的引用。 如果更改第4行,則讓它在圖形中Point的x铡溪,y坐標(biāo)處打印不同的字符漂辐。
class Point:
""" Point class for representing and manipulating x,y coordinates. """
printed_rep = "*"
def __init__(self, initX, initY):
self.x = initX
self.y = initY
def graph(self):
rows = []
size = max(int(self.x), int(self.y)) + 2
for j in range(size-1) :
if (j+1) == int(self.y):
special_row = str((j+1) % 10) + (" "*(int(self.x) -1)) + self.printed_rep
rows.append(special_row)
else:
rows.append(str((j+1) % 10))
rows.reverse() # put higher values of y first
x_axis = ""
for i in range(size):
x_axis += str(i % 10)
rows.append(x_axis)
return "\n".join(rows)
p1 = Point(2, 3)
p2 = Point(3, 12)
print(p1.graph())
print()
print(p2.graph())
4
3 *
2
1
01234
3
2 *
1
0
9
8
7
6
5
4
3
2
1
01234567890123
為了能夠推斷出類變量和實(shí)例變量,了解python解釋器使用的規(guī)則會(huì)很有幫助棕硫。 這樣髓涯,您就可以在心中模擬解釋器的工作
當(dāng)解釋器看到形式為 <obj>.<varname>
的表達(dá)式時(shí),它:
<instancename>.<instance_var/class_methods/class_var>
注 類內(nèi)定義的 methods 也被稱作 類變量 class variable
- 先搜索
dot
后面的內(nèi)容是不是對(duì)應(yīng)實(shí)例<instancename>
里的 實(shí)例變量- 如果找不到實(shí)例變量饲帅,則檢查該類是否具有類變量复凳。 如果是這樣瘤泪,它將使用該值灶泵。
- 如果找不到實(shí)例或類變量,則會(huì)創(chuàng)建運(yùn)行時(shí)錯(cuò)誤(實(shí)際上对途,它會(huì)先進(jìn)行其他檢查赦邻,您將在下一章中進(jìn)行了解)
當(dāng)解釋器看到格式為<obj>.<varname> = <expression>
的賦值語(yǔ)句時(shí),它將:
- 計(jì)算右側(cè)的表達(dá)式以產(chǎn)生一些python對(duì)象实檀;
- 然后將
<obj>
的實(shí)例變量<varname>
綁定到右側(cè)的python對(duì)象惶洲。 請(qǐng)注意,這種形式的賦值語(yǔ)句從不設(shè)置類變量膳犹。 它僅設(shè)置實(shí)例變量恬吕。never sets the class variable; it only sets the instance variable.
為了設(shè)置類變量,您可以在類定義的頂層使用形式為
<varname> = <expr>
的賦值語(yǔ)句须床,就像上面代碼中的第4行一樣铐料,設(shè)置類變量printed_rep
。
舉例:p1.graph()
is evaluated by:
- looking up p1 and finding that it’s an instance of Point
- looking for an instance variable called graph in
p1
, but not finding one- looking for a class variable called graph in
p1
’s class, the Point class; it finds a function/method object- Because of the () after the word graph, it invokes the function/method object, with the parameter self bound to the object
p1
points to.
20.11. 編寫(xiě)class之前 關(guān)于類和實(shí)例的思考
您還可以使用自定義類來(lái)保存數(shù)據(jù)——例如豺旬,通過(guò)向REST API發(fā)出請(qǐng)求而獲得的數(shù)據(jù)钠惩。
在決定定義一個(gè)新類之前,需要牢記一些注意事項(xiàng)族阅,以及應(yīng)該問(wèn)自己的問(wèn)題:
-
First is, what kind of data you want to represent with your class? 你想要用 class 表示什么數(shù)據(jù)篓跛?
Is it a list of songs?
Is it a list of students?
A list of cars et cetera.
-
what does one particular instance represent of this class? class 類的一個(gè)具體 instance 代表什么?
So if it's a list of songs, one particular instance might represent a song.
One particular instance of a list of students might represent one particular student
-
what are the instance variables? What's unique to every instance that I might have? 實(shí)例具有的實(shí)例變量包括哪些坦刀?
So if it's a list of students, it might be something like a name, a student ID.
If it's a list of songs, it might be the artist, the track name, the length, et cetera.
-
what methods might you actually want? 你想要定義什么樣的類內(nèi)方法愧沟?
So, if every instance is a particular song, then you might have a method, for example, to paying an external API to get the lyrics for that song.
If it's a student, you might want to have a method to, for example, send a message to that student.
what does the printed representation of an instance look like? 實(shí)例的打印表示格式是什么樣的?
((This question will help you determine how to write the __str__
method.)
So if I print out a particular song,
then I might want to print out the track name,
and then the album name, and maybe the length.
請(qǐng)記住鲤遥,類定義(如函數(shù)定義)是對(duì)類的每個(gè)實(shí)例應(yīng)具有的內(nèi)容的一般描述(每個(gè)Point
都有一個(gè)x和一個(gè)y)
而類實(shí)例是特定的: 例如沐寺,具有特定 x 和 y 的點(diǎn)。您可能有一個(gè) x 值為 3渴频、y 值為 2 的點(diǎn)芽丹,因此對(duì)于類Point
的特定實(shí)例,您應(yīng)該將 3 和 2 傳遞給構(gòu)造函數(shù)__init__
方法卜朗,就像您在上一節(jié)中看到的那樣:new_point = Point(3拔第,2)
咕村。
20.13. A Tamagotchi Game 一個(gè)案例
還有很多有趣的方式可以使用用戶定義的類,這些類不涉及來(lái)自互聯(lián)網(wǎng)的數(shù)據(jù)蚊俺。 讓我們以比Point類更有趣的方式將所有這些機(jī)制組合在一起懈涛。 還記得小小的電子寵物Tamagotchis嗎? 隨著時(shí)間的流逝泳猬,他們會(huì)變得饑餓或無(wú)聊批钠。 您必須清理它們,否則他們會(huì)生病得封。 您只需在設(shè)備上單擊幾個(gè)按鈕即可完成所有操作埋心。
我們將對(duì)此做一個(gè)簡(jiǎn)化的基于文本的版本。 在您的問(wèn)題集中以及有關(guān)繼承<chap_inheritance>的章節(jié)中忙上,我們將進(jìn)一步擴(kuò)展拷呆。
First, let’s start with a class Pet
. Each instance of the class will be one electronic pet for the user to take care of. Each instance will have a current state, consisting of three instance variables:
- hunger, an integer
- boredom, an integer
- sounds, a list of strings, each a word that the pet has been taught to say
方法:
-
__init__
方法:在__init__
方法中,將hunger
和boredom
初始化為0到各自閾值之間的隨機(jī)值疫粥。sounds
實(shí)例變量被初始化為具有相同名稱的class變量的副本茬斧。我們復(fù)制列表的原因是我們將執(zhí)行破壞性操作(destructive operations)(將新聲音添加到列表中)。 如果我們不進(jìn)行復(fù)制梗逮,那么那些destructive operations將影響到 class variable 指向的列表项秉, and thus teaching a sound to any of the pets would teach it to all instances of the class!
clock_tick
方法:有一個(gè)clock_tick
方法,它僅增加hunger
和boredom
實(shí)例變量慷彤,從而模擬了隨著時(shí)間的流逝娄蔼,寵物變得越來(lái)越無(wú)聊和饑餓的想法。__str__
方法:可產(chǎn)生寵物當(dāng)前狀態(tài)的字符串表示形式瞬欧,特別是它是否無(wú)聊或饑餓或是否快樂(lè)贷屎。 如果無(wú)聊實(shí)例變量大于閾值(設(shè)置為類變量),這很無(wú)聊艘虎。-
teach()
方法/hi()
方法:為了緩解無(wú)聊唉侄,寵物主人可以使用teach()
方法教寵物一個(gè)新單詞,或者使用hi()
方法與寵物互動(dòng)野建。作為對(duì)
teach()
的回應(yīng)属划,寵物將新單詞添加到單詞列表中。作為對(duì)
hi()
方法的響應(yīng)候生,它打印出一個(gè)它知道的單詞同眯,從已知單詞列表中隨機(jī)挑選一個(gè)。
hi()
和teach()
都會(huì)調(diào)用reduce_boredom()
方法唯鸭。它通過(guò)從類變量boredom_decrement
中讀取的量來(lái)減少 boredom 狀態(tài)须蜗。boredom 狀態(tài)永遠(yuǎn)不會(huì)低于0。 reduce_boredom()
方法feed()
方法:To relieve hunger, we call thefeed()
method.
from random import randrange
class Pet():
# 類變量
boredom_decrement = 4
hunger_decrement = 6
boredom_threshold = 5
hunger_threshold = 10
sounds = ['Mrrp']
# 構(gòu)造函數(shù)
# 默認(rèn)參數(shù)
# 隨機(jī)函數(shù)的使用
# 列表復(fù)制
def __init__(self, name = 'Kitty'):
self.name = name
self.hunger = randrange(self.hunger_threshold)
self.boredom = randrange(self.boredom_threshold)
self.sounds = self.sounds[:] # copy the class attribute, so that when we make changes to it, we won't affect the other Pets in the class
# 調(diào)用實(shí)現(xiàn)時(shí)間增長(zhǎng) 對(duì)應(yīng)的實(shí)例變量 無(wú)聊和饑餓值增加
def clock_tick(self):
self.bredom += 1
self.hunger += 1
# 情緒
def mood(self):
if self.hunger <= self.hunger_threshold and self.boredom <= self.boredom_threshold:
return 'happy'
elif self.hunger > self.hunger_threshold:
return 'hungery'
else:
return 'bored'
# print 字符表示
def __str__(self):
state = " I'm " + self.name + ". "
state += ' I feel ' + self.mood() + '. '
return state
# 隨機(jī)輸出一個(gè) 已有的 詞并減少 無(wú)聊值
def hi(self):
print(self.sounds[randrange(len(self.sounds))])
self.reduce_boredom
def feed(self):
self.reduce_hunger()
# 減少饑餓值并保證最小是0
def reduce_hunger(self):
self.hunger = max(0, self.hunger - self.hunger_decrement)
def reduce_boredom(self):
self.boredom = max(0, self.boredom - self.boredom_decrement)
p1 = Pet("Fido") # 創(chuàng)建一個(gè) Pet 實(shí)例 默認(rèn)實(shí)例變量以及命名
print(p1) # 看 __str__
for i in range(10): # 循環(huán)10次, 對(duì) 這個(gè)instance 執(zhí)行 時(shí)間流逝
p1.clock_tick()
print(p1) # 查看現(xiàn)在的狀態(tài)
p1.feed() # 對(duì)p1 執(zhí)行 feed +
p1.hi()
p1.teach("Boo")
for i in range(10):
p1.hi()
print(p1)
如果你想通過(guò)編寫(xiě)python代碼來(lái)與寵物互動(dòng)明肮,那太好了菱农。讓我們做一個(gè)非程序員可以玩的游戲。
我們將使用監(jiān)聽(tīng)器循環(huán)模式(Loop <chap_listener>
pattern)柿估。在每次迭代中循未,我們將顯示一個(gè)文本提示,提醒用戶哪些命令可用秫舌。
用戶將有一個(gè)寵物列表的妖,每個(gè)寵物都有一個(gè)名字。用戶可以發(fā)布一個(gè)命令來(lái)收養(yǎng)一只新寵物足陨,這將創(chuàng)建一個(gè)新的寵物實(shí)例嫂粟。或者钠右,用戶可以通過(guò)問(wèn)候赋元、教導(dǎo)或喂食命令與現(xiàn)有的寵物互動(dòng)。
The user will have a list of pets, each with a name. The user can issue a command to adopt a new pet, which will create a new instance of Pet. Or the user can interact with an existing pet, with a Greet, Teach, or Feed command.
不管用戶做什么飒房,只要輸入一個(gè)命令,他們所有的寵物的時(shí)鐘都會(huì)滴答作響媚值。小心狠毯,如果你有太多的寵物,你將無(wú)法讓他們都滿意褥芒!