目錄
編寫類時,定義一大類對象都有的通用行爲,基於類創建對象時,每個對象都自動具備這種通用行爲,然後可根據需要賦予每個對象獨特的個性。
根據類來創建對象被稱爲實例化。
一、創建和使用類
e.g 創建Dog類。根據Dog類創建的每個實例都將存儲名字和年齡,賦予每條小狗蹲下(sit())和打滾(roll_over())的能力:
class Dog():
"""一次模擬小狗的簡單嘗試"""
def __init__(self, name, age):
"""初始化屬性name和age"""
self.name = name
self.age = age
def sit(self):
"""模擬小狗命令時蹲下"""
print(self.name.title() + " is now sitting.")
def roll_over(self):
"""模擬小狗被命令時打滾"""
print(self.name.title() + " rolled over!")
類的名稱需要首字母大寫。
1. 方法__init__()
類中的函數稱爲方法,有關函數的一切都適用於方法,唯一的差別在於調用方法的方式。
每次根據Dog類創建新實例時,Python都會自動運行方法__init__()。
形參self必不可少,且必須位於其他形參的前面;Python調用方法__init__()來創建Dog實例時,將自動傳入實參self,每個與類相關聯的方法調用都自動傳遞實參self,它是一個指向實例本身的引用,讓實例能夠訪問類中的屬性和方法。e.g 創建Dog實例時,Python將調用Dog類的方法__init__(),我們只需提供最後兩個形參(name、age),self會自動傳遞。
2. self.name
以self爲前綴的變量可供類中的所有方法使用,也可以通過類的任何實例來訪問這些變量。self.name = name獲取存儲在形參name中的值,並將其存儲到變量name中,然後該變量被關聯到當前創建的實例。像這樣,通過實例訪問的變量稱爲屬性。
二、根據類創建實例
class Dog():
---snip---
my_dog = Dog('while',6)
print("My dog's name is " + my_dog.name.title() +".")
print("My dog is " + str(my_dog.age) +" years old.")
- 方法__init__()創建一個表示特定小狗的實例,並使用我們提供的值來設置屬性name和age。
- 方法__init__()並未顯式地包含return語句,但python自動返回一個表示這條小狗的實例,並將該實例存儲在變量my_dog中
- 通常認爲首字母大寫的名稱(e.g Dog)爲類,而小寫的名稱(e.g my_dog)爲根據類創建的實例。
2.1 訪問屬性
句點表示法。
my_dog.name
my_dog.age
2.2 調用方法
根據Dog類創建實例後,可以使用句點表示法來調用Dog類中定義的任何方法。
my_dog.sit()
my_dog.roll_over()
2.3 創建多個實例
可根據需求創建任意數量的實例,前提是將每個實例都存儲在不同的變量中,或佔用列表或字典的不同位置。
三、使用類和實例
修改實例的屬性,可以直接修改也可以編寫方法以特定的方式修改。
3.1 Car類
class Car():
def __init__(self, make, model, year):
"""初始化描述汽車的屬性"""
self.make = make
self.model = model
self.year = year
def get_descriptive_name(self):
"""返回整潔的描述性信息"""
long_name = str(self.year) + ' ' + self.make + ' ' + self.model
return long_name.title()
my_new_car = Car('audi','a4',2016)
print(my_new_car.get_descriptive_name())
3.2 給屬性指定默認值
類中的每個屬性都必須有初始值,哪怕這個值是0或空字符串。在有些情況下,如設置默認值時,在方法__init__()內指定這種初始值時是可行的,如果對某個屬性這樣做了,就無需包含爲它提供初始值的形參。
e.g 添加一個名爲odometer_reading的屬性,其初始值總是0,同時添加一個read_odometer()的方法,用於讀取汽車的里程錶。
class Car():
def __init__(self, make, model, year):
"""初始化描述汽車的屬性"""
self.make = make
self.model = model
self.year = year
self.odometer_reading = 0
def get_descriptive_name(self):
"""返回整潔的描述性信息"""
long_name = str(self.year) + ' ' + self.make + ' ' + self.model
return long_name.title()
def read_odometer(self):
"""打印汽車的里程"""
print("This car has " + str(self.odometer_reading) + " miles on it.")
my_new_car = Car('audi','a4',2016)
print(my_new_car.get_descriptive_name())
my_new_car.read_odometer()
屬性odometer_reading被賦予的初始值,沒有包含在__init__()方法的形參中。
3.3 修改屬性的值
3.3.1 直接修改屬性的值
通過實例直接訪問。
class Car():
---snip---
my_new_car = Car('audi','a4',2016)
print(my_new_car.get_descriptive_name())
# 直接使用句點表示法訪問並設置汽車的屬性odometer_reading,並將其設置爲23
my_new_car.odometer_reading = 23
my_new_car.read_odometer()
3.3.2 通過方法修改屬性的值
無需直接訪問屬性,可將值傳遞給一個方法,由它在內部進行更新。
class Car():
---snip---
def update_odometer(self,mileage):
self.odometer_reading = mileage
my_new_car = Car('audi','a4',2016)
print(my_new_car.get_descriptive_name())
my_new_car.update_olometer(23)
my_new_car.read_odometer()
對方法update_odometer()進行擴展,禁止將里程錶讀數往回調。
def update_odometer(self, mileage):
"""
將里程錶讀數設置爲指定的值
禁止將里程錶讀數往回調
"""
if mileage >= self.odometer_reading:
self.odometer_reading = mileage
else:
print("You can't roll back an odometer!")
3.3.3 通過方法對屬性的值進行遞增
e.g 將屬性值遞增特定的量,而不是將其設置爲全新的值。
假如我們購買了一輛二手車,且從購買到登記期間增加了100英里的里程。
class Car():
---snip---
def update_odometer(self, mileage):
---snip---
def increment_odometer(self,miles):
"""將里程錶讀數增加指定的量"""
if miles >= 0:
self.odometer_reading += miles
else:
print("You can't increase the minus odometer!"
my_used_car = Car('subaru','outback',2013)
print(my_used_car.get_descriptive_name())
my_used_car.update_odometer(23500)
my_used_car.read_odometer()
my_used_car.increment_odometer(100)
my_used_car.read_odometer()
四、繼承
- 如果要編寫的類是另一個現成類的特殊版本,可以使用繼承;
- 一個類繼承另一個類,它將自動獲得另一個類的所有屬性和方法;
- 原有的類稱爲父類,新類稱爲子類,子類繼承了父類的所有屬性和方法,同時還可以定義自己的屬性和方法
4.1 子類的方法 __init__()
e.g 模擬電動汽車。創建一個簡單的ElecticCar類版本,具備Car類的所有功能。
class Car():
def __init__(self, make, model, year):
"""初始化描述汽車的屬性"""
self.make = make
self.model = model
self.year = year
self.odometer_reading = 0
def get_descriptive_name(self):
"""返回整潔的描述性信息"""
long_name = str(self.year) + ' ' + self.make + ' ' + self.model
return long_name.title()
def read_odometer(self):
"""打印汽車的里程"""
print("This car has " + str(self.odometer_reading) + " miles on it.")
def update_odometer(self, mileage):
"""
將里程錶讀數設置爲指定的值
禁止將里程錶讀數往回調
"""
if mileage >= self.odometer_reading:
self.odometer_reading = mileage
else:
print("You can't roll back an odometer!")
def increment_odometer(self, miles):
"""將里程錶讀數增加指定的量"""
self.odometer_reading += miles
class ElectricCar(Car):
"""電動汽車的獨特之處"""
def __init__(self, make, model, year):
""" 初始化父類的屬性"""
super().__init__(make,model,year)
my_tesla = ElectricCar('tesla','model s',2016)
print(my_tesla.get_descriptive_name())
- 創建子類時,父類必須包含在當前文件中,且位於子類的前面;
- 定義子類時,必須在括號內指定父類的名稱 class ElectricCar(Car);
- super()是一個特殊的函數,幫助Python將父類和子類關聯起來,super().__init__(make, model, year)調用父類的方法__init__(),讓ElectricCar實例包含父類的所有屬性,父類也稱爲超類(super因此得名)。
4.2 Python 2.7中的繼承
class Car(object):
def __init__(self, make, model, year):
---snip---
class ElectricCar(Car):
def __init__(self, make, model, year):
super(ElectricCar,self).__init__(make, model, year)
---snip---
- super()需要兩個實參:子類名和對象self
- 在Python2.7中使用繼承時,務必在定義父類時在括號內指定object
4.3 給子類定義屬性和方法
讓一個類繼承另一個類後,可添加區分子類和父類所需的新屬性和方法
e.g 添加電動汽車的特有屬性(電瓶),以及一個描述該屬性的方法;存儲電瓶容量並編寫一個打印電瓶描述的方法。
class Car():
---snip---
class ElectricCar(Car):
def __init__(self, make, model, year):
"""
電動汽車的獨特之處
初始化父類的屬性,再初始化電動汽車特有的屬性
"""
super().__init__(make, model, year)
self.battery_size = 70
def describe_battery(self):
"""打印一條描述電瓶容量的消息"""
print("This car has a " + str(self.battery_size) + "-kWh battery.")
my_tesla = ElectricCar('tesla','model s',2016)
print(my_tesla.get_descriptive_name())
my_tesla.describe_battery()
4.4 重寫父類的方法
- 對於父類的方法,只要它不符合子類模擬的實例的行爲,都可對其進行重寫;
- 重寫,即子類中的方法與父類中的方法同名,這樣Python不會考慮父類中的這個方法,而只關注子類中定義的方法。
4.5 將實例用作屬性
可以將類的一部分作爲一個獨立的類提取出來,即將一個大型類拆分成多個協同工作的小類。
e.g 將針對汽車電瓶的屬性和方法,放到另一個類Battery()中,並將Battery實例用作ElectricCar類的一個屬性。
class Car():
---snip---
class Battery():
"""模擬電動汽車的電瓶"""
def __init__(self, battery_size=70):
self.battery_size = battery
def describe_battery():
print("This car has a " + str(battery_size) +"-kWh battery.")
class ElectricCar():
def __init__(self, make, model, year):
super().__init__(make, model, year)
self.battery = Battery(80)
my_tesla = ElectricCar('Tesla','model s',2016)
print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery()
e.g 再給Battery類添加一個方法,根據其電瓶容量報告汽車的續航里程。
class Car():
--snip--
class Battery():
--snip--
def get_range(self):
"""打印一條消息,指出電瓶的續航里程"""
if self.battery_size == 70:
range = 240
elif self.battery_size ==85:
range = 270
message = "This car can go approximately " + str(range)
message +=" miles on a full charge."
print(message)
class ElectricCar(Car):
--snip--
my_tesla = ElectricCar('Tesla','model s',2016)
print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery()
my_tesla.battery.get_range()
五、導入類
5.1 導入單個類
可以將類Car 放在一個單獨的文件car.py中,然後使用語句:
from car import Car
導入類Car。
5.2 在一個模塊中存儲多個類
import 後加不同的類名,導入不同的類。
from car import Car
from car import ElectricCar
from car import Battery
from car import ElectricCar
my_tesla = ElectricCar('tesla','model s',2016)
print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery()
my_tesla.battery.get_range()
5.3 從一個模塊中導入多個類
可根據需要在程序文件中導入任意數量的類,使用逗號,分隔各個類。
from car import Car,ElectricCar
5.4 導入整個模塊
使用句點表示法訪問需要的類。
import car
my_car = car.Car('audi','a4',2016)
5.5 導入模塊中的所有類
要導入模塊中的每個類,使用*
from module_name import *
- 不建議使用這種方法,容易引發名稱錯誤
- 需要從一個模塊導入很多類時,最好導入整個模塊,然後使用句點法來訪問類module_name.class_name
5.6 在一個模塊中導入另一個模塊
- 有時,需要將類分散到多個模塊中,以免模塊太大,或在同一個模塊中存儲不相關的類;
- 將類存儲在多個模塊中時,一個模塊中的類kennel依賴於另一個模塊中的類,在這種情況下就有必要在前一個模塊中導入必要的類
e.g 將Car類存儲在car.py模塊中,將ElectricCar和Battery類存儲在另一個模塊electric_car.py中。
from car import Car
class Battery():
--snip--
class ElectricCar(Car):
--snip--
六、Python標準庫
- 模塊collection中的一個類OrderedDict,創建的字典與普通字典的區別是可以記錄鍵-值對的添加順序
- 模塊random包含以各種方式生成隨機數的函數,其中randint()返回位於指定範圍內的整數。
e.g 創建一個Die類,它包含一個名爲sides的屬性,該屬性的默認值爲6,編寫一個名爲roll_die()的方法,它位於1和骰子面數之間的一個隨機數,創建一個6面的骰子,擲10次。創建一個10面和一個20面的骰子,並將他們都擲10次。
from random import randint
class Die():
def __init__(self, sides=6):
self.sides = sides
def roll_die(self):
print(str(randint(1,self.sides)))
my_die_6 = Die()
print("\n Roll Die-6:")
for i in range(0,10):
my_die_6.roll_die()
my_die_10 = Die(10)
print("\nRoll Die-10:")
for i in range(0,10):
my_die_10.roll_die()
my_die_20 =Die(20)
print('\nRoll Die-20:')
for i in range(0,10):
my_die_20.roll_die()
七、類編碼風格
- 類名採用駝峯命名法,每個單詞的首字母大寫,單詞之間不使用下劃線;實例名和模塊名採用小寫格式,單詞之間使用下劃線;
- 類定義後包含文檔字符串簡要描述類的功能;
- 在類中,使用一個空行分隔方法;在模塊中,使用兩個空行分隔類;
- 需要同時導入標準庫中的模塊和自己編寫的模塊時,先編寫導入標準庫模塊的import語句,添加一個空行,再編寫導入自己編寫的模塊的import語句。