例9.1~例9.53
補充:類名爲有效的標識符,一般爲多個單詞組成的名稱,每個單詞除第一個字母大寫外,其餘的字母均小寫
一:類對象和實例對象
例9.1(創建類對象和實例對象)
>>> class Person:
pass
>>> p = Person()
>>> print(Person, type(Person), id(Person))
<class '__main__.Person'> <class 'type'> 2096437524072
>>> print(p, type(p), id(p))
<__main__.Person object at 0x000001E81DA88BC8> <class '__main__.Person'> 2096441625544
例9.2(實例對象的創建和使用)
- Python創建實例對象的方法無須使用關鍵字new,而是直接像調用函數一樣調用類對象並傳遞參數,因此類對象是可調用對象(Callable)
- 在Python內置函數中,bool、float、int、str、list、dict、set等均爲可調用內置類對象
>>> c = complex(1, 2)
>>> c.conjugate()
(1-2j)
>>> c.real
1.0
二:屬性
例9.3(定義實例屬性)
Python變量不需要聲明,可直接使用。所以建議用戶在類定義的開始位置初始化類屬性,或者在構造函數
__init__()
中初始化實例屬性
>>> class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def say_hi(self):
print("您好,我叫", self.name)
>>> p = Person('zgh', 18)
>>> p.say_hi()
您好,我叫 zgh
>>> print(p.age)
18
例9.4(定義類對象屬性)
類屬性如果通過
obj.屬性名
來訪問,則屬於該實例地實例屬性
>>> class Person:
count = 0
name = "Person"
>>> Person.count += 1
>>> print(Person.count)
1
>>> print(Person.name)
Person
>>> p1 = Person(); p2 = Person()
>>> print(p1.name, p2.name)
Person Person
>>> Person.name = "僱員"
>>> print(p1.name, p2.name)
僱員 僱員
>>> p1.name = '員工'
>>> print(p1.name, p2.name)
員工 僱員
例9.5(私有屬性)
Python類的成員沒有訪問控制限制,這與其他面嚮對象語言不同
通常約定以兩個下劃線開頭,但是不以兩個下劃線結束的屬性是私有的(private),其它爲公共的(public)
>>> class Person:
__name = 'class Person'
def get_name():
print(Person.__name)
>>> Person.get_name()
class Person
>>> Person.__name
Traceback (most recent call last):
File "<pyshell#46>", line 1, in <module>
Person.__name
AttributeError: type object 'Person' has no attribute '__name'
例9.6、9.7、9.8(property裝飾器)
- 面向對象編程的封裝性原則要求不直接訪問類中的數據成員
- 在Python中可以定義私有屬性,然後定義相應的訪問該私有屬性的函數,並使用@property裝飾器來裝飾這些函數
- 程序可以把函數“當作”屬性訪問,從而提供更加友好的訪問方式
>>> class Person:
def __init__(self, name):
self.__name = name
@property
def name(self):
return self.__name
>>> p = Person('zgh666')
>>> print(p.name)
zgh666
嘗試了一個想法(如果對象也有一個同名的屬性,會怎麼樣?):
>>> class Person:
name = 'zgh'
def __init__(self, name):
self.__name = name
@property
def name(self):
return self.__name
>>> p = Person('zgh666')
>>> print(p.name)
zgh666
很顯然,返回的是裝飾器修飾的name函數
@property裝飾器默認提供一個只讀屬性,如果需要,可以使用對應的getter、setter和deleter裝飾器實現其他訪問器函數
>>> class Person:
def __init__(self, name):
self.__name = name
@property
def name(self):
return self.__name
@name.setter
def name(self, value):
self.__name = value
@name.deleter
def name(self):
del self.__name
>>> p = Person('zgh')
>>> p.name = 'Zhanggguohao'
>>> print(p.name)
Zhanggguohao
property(fget=None, fset=None, fdel=None, doc=None)
>>> class Person:
def __init__(self, name):
self.__name = name
def getname(self):
return self.__name
def setname(self, value):
self.__name = value
def delname(self):
del self.__name
name = property(getname, setname,delname, "I'm the 'name' property.")
>>> p = Person('zgh')
>>> print(p.name)
zgh
>>> p.name = 'zgh666'
>>> print(p.name)
zgh666
例9.9(自定義屬性)
在Python中,可以賦予一個對象自定義的屬性,即類定義中不存在的屬性。對象通過特殊屬性
__dict__
存儲自定義屬性
>>> class Person:
pass
>>> p = Person()
>>> p.name = 'custom name'
>>> p.name
'custom name'
>>> p.__dict__
{'name': 'custom name'}
通過重載
__getattr__
和__setattr__
可以攔截對成員的訪問,從而自定義屬性的行爲
__getattr__()
只有在訪問不存在的成員時纔會被調用__getattribute__()
攔截所有的(包括不存在)獲取操作__setattr__()
設置屬性__delattr__()
刪除屬性
>>> class CustomAttribute(object):
def __init__(self):
pass
def __getattribute__(self, name):
return str.upper(object.__getattribute__(self, name))
def __setattr__(self, name, value):
object.__setattr__(self, name, str.strip(value))
>>> p = CustomAttribute()
>>> p.firstname = ' mary'
>>> print(p.firstname)
MARY
三:方法
對象實例方法的第一個參數一般爲self,但是用戶調用時不需要也不能給該參數傳值
例9.11(靜態方法:攝氏溫度與華氏溫度之間的相互轉換)
class TemperatureConverter:
@staticmethod
def c2f(t_c):
return (float(t_c) * 9/5) + 32
@staticmethod
def f2c(t_f):
return ((float(t_f) - 32) * 5/9)
print("1. 從攝氏溫度到華氏溫度.")
print("2. 從華氏溫度到攝氏溫度.")
choice = int(input("請選擇轉換方向:"))
if choice == 1:
t_c = float(input("請輸入攝氏溫度:"))
t_f = TemperatureConverter.c2f(t_c)
print("華氏溫度爲:{0:.2f}".format(t_f))
elif choice == 2:
t_f = float(input("請輸入華氏溫度:"))
t_c = TemperatureConverter.f2c(t_f)
print("攝氏溫度爲:{0:.2f}".format(t_c))
else:
print("無此選項,只能選擇1或2")
輸出:
====================== RESTART: D:\zgh\desktop\test.py ======================
1. 從攝氏溫度到華氏溫度.
2. 從華氏溫度到攝氏溫度.
請選擇轉換方向:1
請輸入攝氏溫度:30
華氏溫度爲:86.00
>>>
====================== RESTART: D:\zgh\desktop\test.py ======================
1. 從攝氏溫度到華氏溫度.
2. 從華氏溫度到攝氏溫度.
請選擇轉換方向:2
請輸入華氏溫度:70
攝氏溫度爲:21.11
>>>
補充一個很有意思的代碼:
(包含了很多知識點)
TempStr = input("請輸入帶有符號的溫度值:")
if TempStr[-1] in ['F', 'f']:
C = (eval(TempStr[0:-1]) - 32) / 1.8
print("轉化後的溫度是{:.2f}C".format(C))
elif TempStr[-1] in ['C', 'c']:
F = 1.8 * eval(TempStr[0:-1]) + 32
print("轉化後的溫度是{:.2f}F".format(F))
else:
print("輸入格式錯誤")
例9.12(類方法)
類方法的第一個參數爲cls,但是調用時用戶不需要也不能給該參數傳值
>>> class Person:
classname = "zgh"
def __init__(self, name):
self.name = name
#實例方法
def f1(self):
print(self.name)
#靜態方法
@staticmethod
def f2():
print("static")
#類方法
@classmethod
def f3(cls):
print(cls.classname)
>>> p = Person("666")
>>> p.f1()
666
>>> Person.f2()
static
>>> p.f2()
static
>>> Person.f3()
zgh
>>> p.f3()
zgh
>>> Person.f1()
Traceback (most recent call last):
File "<pyshell#23>", line 1, in <module>
Person.f1()
TypeError: f1() missing 1 required positional argument: 'self'
類名不能訪問實例方法
補充:
- 無論是靜態方法還是類方法一般通過類名來訪問,也可以通過對象實例來調用
- 在靜態方法中訪問對象實例會導致錯誤
- 在類方法中訪問對象實例屬性會導致錯誤
例9.15(__init__()
方法、__new__()
方法、__del__()
方法)
__init__()
構造函數,用於執行類的實例的初始化工作。在創建完對象後調用,初始化當前對象的實例,無返回值__new__()
是一個類方法,在創建對象時調用,返回當前對象的一個實例,一般無須重載該方法__del__()
析構函數,用於實現銷燬類的實例所需的操作,如釋放對象佔用的非託管資源(打開的文件、網絡連接等)
在默認情況下,當對象不再被使用時,__del__()
方法運行。由於Python解釋器實現自動垃圾回收,所以無法保證這個方法究竟在什麼時候運行
但通過del
語句可以強制銷燬一個對象實例,從而保證調用對象實例的__del__()
方法
>>> class Person:
count = 0
def __init__(self, name, age):
self.name = name
self.age = age
Person.count += 1
def __del__(self):
Person.count -= 1
def say_hi(self):
print("hello, i'm ", self.name)
def get_count():
print("count: ",Person.count)
>>> print("count: ", Person.count)
count: 0
>>> p1 = Person('zhangsan', 18)
>>> p1.say_hi()
hello, i'm zhangsan
>>> Person.get_count()
count: 1
>>> p2 = Person('lisi', 28)
>>> p2.say_hi()
hello, i'm lisi
>>> Person.get_count()
count: 2
>>> del p1
>>> Person.get_count()
count: 1
>>> del p2
>>> Person.get_count()
count: 0
例9.16(私有方法)
- 與私有屬性類似,Python約定以兩個下劃線開頭,但不以兩個下劃線結束的方法是私有的
- 不能直接訪問私有的
>>> class Book:
def __init__(self, name, author, price):
self.name = name
self.author = author
self.price = price
def __check_name(self):
if self.name == '': return False
else: return True
def get_name(self):
if self.__check_name(): print(self.name, self.author)
else: print("No Value")
>>> b = Book("Python", 'zgh', 666)
>>> b.get_name()
Python zgh
>>> b.__check_name()#直接調用私有方法,非法
Traceback (most recent call last):
File "<pyshell#66>", line 1, in <module>
b.__check_name()
AttributeError: 'Book' object has no attribute '__check_name'
例9.17(方法的重載)
Python本身是動態語言,
- 方法的參數沒有聲明類型(在調用傳值時確定參數的類型)
- 參數的數量由可選參數和可變參數來控制
- 故Python對象方法不需要重載,定義一個方法即可實現多種調用,從而實現相當於其他程序設計語言的重載功能
>>> class Person:
def say_hi(self, name=None):
self.name = name
if name ==None: print("hello!")
else: print("hello, i'm ", self.name)
>>> p = Person()
>>> p.say_hi()
hello!
>>> p.say_hi('zgh')
hello, i'm zgh
>>>
注意:在Python類體中定義多個重名的方法雖然不會報錯,但只有最後一個方法有效,所以建議不要定義重名的方法
四:繼承
- Pyhon支持多重繼承,即一個派生類可以繼承多個基類
- 如果在類定義中沒有指定基類,則默認其基類爲
object
- 在聲明派生類時,必須在其構造函數中調用基類的構造函數
例9.19(派生類)
>>> class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def say_hi(self):
print("hello, i'm {0}, {1} olds".format(self.name, self.age))
>>> class Student(Person):
def __init__(self, name, age, stu_id):
Person.__init__(self, name, age)
self.stu_id = stu_id
def say_hi(self):
Person.say_hi(self)
print("i'm student, my student_number is ", self.stu_id)
>>> p =Person('zhangsan',18)
>>> p.say_hi()
hello, i'm zhangsan, 18 olds
>>> s = Student('lisi', 5, '17202030118')
>>> s.say_hi()
hello, i'm lisi, 5 olds
i'm student, my student_number is 17202030118
例9.20(查看類的繼承關係)
多個類的繼承可以形成層次關係,通過類的方法
mro()
或類的屬性__mro__
可以輸出其繼承的層次關係
>>> class A: pass
>>> class B(A): pass
>>> class C(B): pass
>>> class D(A): pass
>>> class E(B,D): pass
>>> A.mro()
[<class '__main__.A'>, <class 'object'>]
>>> B.__mro__
(<class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
>>> C.mro()
[<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
>>>
>>> D.mro()
[<class '__main__.D'>, <class '__main__.A'>, <class 'object'>]
>>>
>>> E.__mro__
(<class '__main__.E'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.A'>, <class 'object'>)
例9.21(類成員的繼承和重寫)
>>> class Dimension:
def __init__(self, x, y):
self.x = x
self.y = y
def area(self):
pass
>>> class Circle(Dimension):
def __init__(self, r):
Dimension.__init__(self, r, 0)
def area(self):
return 3.14 * self.x * self.x
>>> class Rectangle(Dimension):
def __init__(self, w, h):
Dimension.__init__(self, w, h)
def area(self):
return self.x * self.y
>>> d1 = Circle(2.0)
>>> d2 = Rectangle(3.0, 4.0)
>>> print(d1.area(), d2.area())
12.56 12.0
五:對象的特殊方法
- 在Python對象中包含許多以雙下劃線開始和結束的方法,稱之爲特殊方法。
- 特殊方法通常在針對對象的某種操作時自動調用
例9.22(重寫對象的特殊方法)
特殊方法 | 含義 |
---|---|
__init__() ,__del__() |
創建或銷燬對象時調用 |
__setitem__() ,__getitem__() |
按索引賦值、取值 |
__len__() |
對應於內置函數len() |
__repr__(self) |
對應於內置函數repr() |
__str__(self) |
對應於內置函數str() |
__bytes__(self) |
對應於內置函數bytes() |
__format__(self, format_spec) |
對應於內置函數format() |
__bool__(self) |
對應於內置函數bool() |
__hash__(self) |
對應於內置函數hash() |
__dir__(self) |
對應於內置函數dir() |
>>> class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return '{0}, {1}'.format(self.name, self.age)
>>> p = Person('zgh', '18')
>>> print(p)
zgh, 18
例9.23(運算符重載)
特殊方法 | 含義 |
---|---|
__lt__() ,__le__() ,__eq__() |
對應於運算符<,<=,== |
__gt__() ,__ge__() ,__ne__() |
對應於運算符>,>=,!= |
__or__() ,__ror__() ;__xor__() ,__rxor__() ;__and__() ,__rand__() |
對應於運算符|,^,& |
__ior__() ,__ixor__() ,__iand__() |
對應於運算符|=,^=,&= |
__lshift__() ,__rlshift__() ;__rshift__() ,__rrshift__() |
對應於運算符<<,>> |
__ilshift__() ,__irlshift__() ;__irshift__() ,__irrshift__() |
對應於運算符<<=,>>= |
__add__() ,__radd__() ;__sub__() ,__rsub__() |
對應於運算符+,- |
__iaddr__() ,__isub__() |
對應於運算符+=,-= |
__mul__() ,__rmul__() ;__truediv__() ,__rtruediv__() |
對應於運算符*,/ |
__mod__() ,__rmod__() ;__floordiv__() ,__rfloordiv__() |
對應於運算符%,// |
__imul__() ,__idiv__() ,__itruediv__() ,__imod__() ,__ifloordiv__() |
對應於運算符*=,/=,%=,//= |
__pos__() ,__neg__() |
正負號 |
__invert__() |
按位翻轉 |
__pow__() ,__rpow__() ,__ipow__() |
指數運算 |
看了上這麼多特殊方法和運算符的對應,你是否會感到一些奇怪的地方:
r
,某些運算符爲啥子有兩個,然後其中一個的最前面加了一個r
舉個例子就懂了:x.__mul__(y) == x / y
;而x.__rmul__(y) == y / x
- 如果上手去嘗試的話,發現複合賦值運算對應的特殊方法都使用不了(未找到原因,暫過,哪位大佬知道啊,評論區留言)
>>> class MyList:
def __init__(self, *args):
self.__mylist = []
for arg in args:
self.__mylist.append(arg)
def __add__(self, n):
for i in range(0, len(self.__mylist)):
self.__mylist[i] += n
def __sub__(self, n):
for i in range(0, len(self.__mylist)):
self.__mylist[i] -= n
def __mul__(self, n):
for i in range(0, len(self.__mylist)):
self.__mylist[i] *= n
def __truediv__(self, n):
for i in range(0, len(self.__mylist)):
self.__mylist[i] /= n
def __len__(self):
return (len(self.__mylist))
def __repr__(self):
str1 = ''
for i in range(0, len(self.__mylist)):
str1 += str(self.__mylist[i]) + " "
return str1
>>> m = MyList(1, 2, 3, 4, 5)
>>> m+2
>>> m
3 4 5 6 7
>>> m -1
>>> m
2 3 4 5 6
>>> m*4
>>> m
8 12 16 20 24
>>> m/2
>>> m
4.0 6.0 8.0 10.0 12.0
>>> len(m)
5
例9.24(@functools.total_ordering
裝飾器)
實現了
total_ordering
裝飾器後,則是需要實現__eq__()
以及__lt__()
、__le__()
、__gt__()
、__ge__()
中的任意一個,那麼實現其他比較運算符能簡化代碼量
import functools
@functools.total_ordering
class Student:
def __init__(self, firstname, lastname):
self.firstname = firstname
self.lastname = lastname
def __eq__(self, other):
return ((self.lastname.lower(), self.firstname.lower()) ==
(other.lastname.lower(), other.firstname.lower()))
def __lt__(self, other):
return ((self.lastname.lower(), self.firstname.lower()) <
(other.lastname.lower(), other.firstname.lower()))
if __name__ == '__main__':
s1 = Student('Mary', 'Clinton')
s2 = Student('Mary', 'Clinton')
s3 = Student('Charlie', 'Clinton')
print(s1 == s2)
print(s1 > s3)
print(s1 < s3)
輸出:
True
True
False
例9.25(定義了__call__()
方法的對象稱爲可調用對象)
class GDistance:
def __init__(self, g):
self.g = g
def __call__(self, t):
return (self.g * t ** 2)/2
if __name__ == '__main__':
e_gdist = GDistance(9.8)
for t in range(11):
print(format(e_gdist(t), "0.2f"), end = ' ')
輸出:
0.00 4.90 19.60 44.10 78.40 122.50 176.40 240.10 313.60 396.90 490.00
六:對象的引用、淺拷貝和深拷貝
例9.26(對象的引用)
對象的賦值實際上是對象的引用,在創建一個對象並把它賦值給一個變量時,該變量是指向該對象的引用,其id()返回值保持一致
>>> acc10 = ['zgh', ['zgh666', 666]]
>>> acc11 = acc10
>>> id(acc10) == id(acc11)
True
例9.27(對象的淺拷貝)
對象的賦值引用同一個對象(即:不復制對象)
如果要複製對象,可以使用下列方法之一:
- 切片操作:例如acc11[:]
- 對象實例化:例如list(acc11)
- copy模塊的copy()函數:例如copy.copy(acc11)
>>> import copy
>>> acc1 = ['Charlie', ['credit', 0.0]]
>>> acc2 = acc1[:]
>>> acc3 = list(acc1)
>>> acc4 = copy.copy(acc1)
>>> id(acc1), id(acc2), id(acc3), id(acc4)
(2387738921800, 2387738921544, 2387735787656, 2387738922888)
Python複製一般是淺拷貝,即複製對象時,對象內包含的子對象並不複製,而是引用同一個子對象
>>> acc2[0] = 'zgh666'
>>> acc2[1][1] = 100.0
>>> acc1
['Charlie', ['credit', 100.0]]
>>> acc2
['zgh666', ['credit', 100.0]]
>>> id(acc1[1]) == id(acc2[1])
True
例9.28(對象的深拷貝)
使用copy模塊的deepcopy()函數
>>> import copy
>>> acc1 = ['Charlie', ['credit', 0.0]]
>>> acc5 = copy.deepcopy(acc1)
>>> acc5[0] = 'zgh666'
>>> acc5[1][1] = 666
>>> acc1
['Charlie', ['credit', 100.0]]
>>> acc5
['zgh666', ['credit', 666]]
>>> id(acc1) == id(acc5)
False
>>> id(acc1[1]) == id(acc5[1])
False
七:可迭代對象:迭代器和生成器
相對於序列,可迭代對象僅在迭代時產生數據,故可以節省內存空間
- Python語言提供了若干內置可迭代對象,例如:range、map、enumerate、filter、zip
- 在標準庫itertools模塊中包含各種迭代器,這些迭代器非常高效,且內存消耗小
- 在Python中,實現了
__iter__()
的對象是可迭代對象 - 在collections.abc模塊中定義了抽象基類Iterable,使用內置的isinstance()可以判斷一個對象是否爲可迭代對象
>>> import collections.abc
>>> isinstance((1, 2, 3), collections.abc.Iterable)
True
>>> isinstance('Python33', collections.abc.Iterable)
True
>>> isinstance(123, collections.abc.Iterable)
False
- 實現了
__next__()
的對象是迭代器 - 使用迭代器可以實現對象的迭代循環,迭代器讓程序更加通用、優雅、高效,更加Python化。對於大量項目的迭代,使用列表會佔用更多的內存,而使用迭代器可以避免之
>>> [i**2 for i in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> import collections.abc
>>> i1 = (i**2 for i in range(10))
>>> isinstance(i1, collections.abc.Iterator)
True
- 迭代器對象必須實現兩個方法,即
__iter__()
、__next__()
,二者合稱爲迭代器協議 __iter__()
用於返回對象本身,以方便for語句進行迭代__next__()
用於返回下一元素- 內置函數
iter(iterable)
可以返回可迭代對象iterable的迭代器 - 內置函數
next()
調用迭代器__next__()
方法依次返回下一個項目值,如果沒有新項目,則將導致StopIteration
>>> t = ('zgh', '666')
>>> i = iter(t)
>>> next(i)
'zgh'
>>> next(i)
'666'
>>> next(i)
Traceback (most recent call last):
File "<pyshell#72>", line 1, in <module>
next(i)
StopIteration
- 例9.29(使用while循環迭代可迭代對象)
>>> t = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
>>> fetch = iter(t)
>>> while True:
try: i = next(fetch)
except StopIteration: break
print(i, end = ' ')
0 1 2 3 4 5 6 7 8 9
聲明一個類,定義
__iter__()
、__next__()
方法。創建該類的對象既是可迭代對象,也是迭代器
- 例9.30(定義Fib,實現Fibonacci數列)
>>> class Fib:
def __init__(self):
self.a, self.b = 0, 1
def __next__(self):
self.a, self.b = self.b, self.a+self.b
return self.a
def __iter__(self):
return self
>>> fibs = Fib()
>>> for i in fibs:
if i < 1000: print(i, end =', ')
else: break
1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987,
- 在函數定義中,如果使用yield語句代替return返回一個值,則定義了一個生成器函數(generator)
- 生成器函數是一個迭代器,是可迭代對象,支持迭代
>>> def triples(n):
for i in range(n):
yield i*3
>>> f = triples(10)
>>> f
<generator object triples at 0x0000022BF0563DC8>
>>> i = iter(f)
>>> next(i)
0
>>> next(i)
3
>>> for t in f: print(t, end = ', ')
6, 9, 12, 15, 18, 21, 24, 27,
- 例9.31(利用生成器函數創建Fibonacci數列)
>>> def fib():
a,b = 0,1
while True:
a,b = b,a+b
yield a
>>> if __name__ == '__main__':
fibs = fib()
for f in fibs:
if f < 1000: print(f, end = ' ')
else: break
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
- 例9.32(利用生成器函數創建返回m到n之間素數的生成器)
>>> import math
>>> def is_prime(n):
if n < 2: return False
if n == 2: return True
if n % 2 == 0: return False
sqrt_n = int(math.floor(math.sqrt(n)))
for i in range(3, sqrt_n+1, 2):
if n % i == 0: return False
return True
>>> def primes(m, n):
for i in range(m, n+1):
if is_prime(i):
yield i
>>> if __name__ == '__main__':
primes1 = primes(50_0000_0000, 50_0000_0090)
for p in primes1:
print(p, end = ' ')
5000000029 5000000039 5000000059 5000000063
只有長度有限的序列或者實現了
__reversed__()
方法的可迭代對象纔可以使用內置函數reversed()
>>> reversed([1, 2, 3, 4, 5])
<list_reverseiterator object at 0x0000022BF04FE608>
>>> for i in reversed([1, 2, 3, 4, 5]): print(i, end = ' ')
5 4 3 2 1
- 只有長度有限的序列或者實現了
__reversed__()
方法的可迭代對象纔可以使用內置函數reversed()
>>> class Countdown:
def __init__(self, start):
self.start = start
def __iter__(self):
n = self.start
while n > 0:
yield n
n -= 1
def __reversed__(self):
n = 1
while n <= self.start:
yield n
n += 1
>>> if __name__ == '__main__':
for i in Countdown(10): print(i, end = ' ')
for i in reversed(Countdown(10)): print(i, end = ' ')
10 9 8 7 6 5 4 3 2 1 1 2 3 4 5 6 7 8 9 10
- 生成器表達式
生成器表達式的語法和列表解析基本一樣,只不過生成器表達式使用()
代替[]
>>> (i **2 for i in range(10))
<generator object <genexpr> at 0x0000022BF0563F48>
>>> for j in (i **2 for i in range(10)):
print(j, end = ' ')
0 1 4 9 16 25 36 49 64 81
>>> for j in (i **2 for i in range(10) if i %3 == 0):
print(j, end = ' ')
0 9 36 81
- map迭代器和itertools.starmap迭代器
- 我們已經學過了
map(function, iterable, ...)
函數 - 如果函數的參數爲元組,則需要使用
itertools.starmap()
迭代器
>>> import itertools
>>> list(itertools.starmap(pow, [(2,5), (3,2), (10,3)]))
[32, 9, 1000]
- filter迭代器和itertools.filterfalse迭代器
- 我們已經學過了
filter(function, iterable)
函數,若結果爲True,則返回該元素。如果function爲None,則返回元素爲True的元素 itertools.filterfalse(predicate, iterable)
則反之
>>> filter
<class 'filter'>
>>> list(filter(lambda x : x > 0, (-1, 2, -3, 0, 5)))
[2, 5]
>>> list(filter(None, (1, 2, 3, 0, 5)))
[1, 2, 3, 5]
>>>
>>> import itertools
>>> list(itertools.filterfalse(lambda x : x%2, range(10)))
[0, 2, 4, 6, 8]
- zip迭代器和itertools.zip_longest迭代器
zip(*iterables)
用於拼接多個可迭代對象的元素,返回新的可迭代對象。如果各序列的長度不一致,則截取至最小序列長度- 如果需要截取最長的長度,可以使用
itertools.zip_longest(*iterables, fillvalue = None)
迭代器
>>> zip
<class 'zip'>
>>> zip((1,2,3), 'abc', range(3))
<zip object at 0x000001B3CDD758C8>
>>> list(zip((1,2,3), 'abc', range(3)))
[(1, 'a', 0), (2, 'b', 1), (3, 'c', 2)]
>>> list(zip('zgh', range(6,10)))
[('z', 6), ('g', 7), ('h', 8)]
>>>
>>>
>>> import itertools
>>> list(itertools.zip_longest('zgh', range(6,10), fillvalue = '-'))
[('z', 6), ('g', 7), ('h', 8), ('-', 9)]
>>> list(itertools.zip_longest('zgh', range(6,10)))
[('z', 6), ('g', 7), ('h', 8), (None, 9)]
- enumerate(iterable, start = 0)可迭代對象
- 用於枚舉可迭代對象iterable中的元素,返回元素爲元組(計數,元素)的可迭代對象
>>> enumerate
<class 'enumerate'>
>>> list(enumerate('zgh666', start = 666))
[(666, 'z'), (667, 'g'), (668, 'h'), (669, '6'), (670, '6'), (671, '6')]
- 無窮序列迭代器itertools.count、itertools.cycle和itertools.repeat
count(start = 0, step = 1)
從start開始,步長爲step的無窮序列cycle(iterable)
可迭代對象iterable元素的無限重複repeat(object[, times])
重複對象object無數次(若指定times,則重複times次)
>>> from itertools import *
>>> list(zip(count(1), 'zgh666'))
[(1, 'z'), (2, 'g'), (3, 'h'), (4, '6'), (5, '6'), (6, '6')]
>>> list(zip(range(10), cycle('zgh')))
[(0, 'z'), (1, 'g'), (2, 'h'), (3, 'z'), (4, 'g'), (5, 'h'), (6, 'z'), (7, 'g'), (8, 'h'), (9, 'z')]
>>> list(repeat('zgh', 6))
['zgh', 'zgh', 'zgh', 'zgh', 'zgh', 'zgh']
- 累計迭代器itertools.accumulate
accumulate(iterable[, func])
- 如果指定了帶兩個參數的func,則func代替默認的加法運算
>>> import itertools
>>> list(itertools.accumulate(range(1,11)))
[1, 3, 6, 10, 15, 21, 28, 36, 45, 55]
>>> import operator
>>> list(itertools.accumulate(range(1,11), operator.mul))
[1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800]
- 級聯迭代器itertools.chain
chain(*iterables)
- 由於連接所有的可迭代函數,及連接多個可迭代對象的元素,作爲一個序列
- chain的類工廠函數
chain.from_iterable(iterable)
也可以用於連接多個序列
>>> import itertools
>>> list(itertools.chain('zgh',(6,6,6),range(5)))
['z', 'g', 'h', 6, 6, 6, 0, 1, 2, 3, 4]
>>>
>>> list(itertools.chain.from_iterable(['zgh', '666']))
['z', 'g', 'h', '6', '6', '6']
- 選擇壓縮迭代器itertools.compress
compress(data, selectors)
- 根據selectors的元素(True/False),返回True對應的data序列中的元素。當data序列或者selectors終止時停止判斷
>>> import itertools
>>> list(itertools.compress('abcdef', [1,0,1,0,1,1]))
['a', 'c', 'e', 'f']
- 截取迭代器itertools.dropwhile和itertools.takewhile
dropwhile(predicate, iterable)
根據條件函數predicate處理可迭代對象的每個元素,丟棄iterable的元素,直到條件函數的結果爲False(補充:這裏不應該是True,書上有誤)takewhile(predicate, iterable)
根據條件函數predicate處理可迭代對象的每個元素,返回iterable的元素,直到條件函數的結果爲False
>>> import itertools
>>> list(itertools.dropwhile(lambda x : x < 5, [1, 4, 6, 4, 1]))
[6, 4, 1]
>>> list(itertools.takewhile(lambda x : x < 5, [1, 4, 6, 4, 1]))
[1, 4]
- 切片迭代器itertools.islice
islice(iterable, stop)
islice(iterable, start, stop[, step])
- 返回可迭代對象iterable的切片,從索引位置start(第一個元素爲0)開始到stop(不包括)結束,步長爲step(默認爲1)。如果stop爲None,則操作直到結束
>>> import itertools
>>> list(itertools.islice('ABCDEF', 2))
['A', 'B']
>>> list(itertools.islice('ABCDEF', 2, 4))
['C', 'D']
>>> list(itertools.islice('ABCDEF', 2, None))
['C', 'D', 'E', 'F']
>>> list(itertools.islice('ABCDEF', 0, None, 2))
['A', 'C', 'E']
- 分組迭代器itertools.groupby
groupby(iterable, key = None)
- iterable爲待分組的可迭代對象
- 可選的key爲用於計算鍵值的函數,默認爲None,即鍵值爲元素本身值
- 返回的結果爲迭代器,其元素爲(key, group),其中key爲分組的鍵值,group爲iterable中具有相同key值的元素的集合的子迭代器
- 一般與排序聯合使用
>>> import itertools
>>> data = [1, -2, 0, 0, -1, 2, 1, -1, 2, 0, 0]
>>> data1 = sorted(data, key = abs)
>>> for k, g in itertools.groupby(data, key = abs):
print(k, list(g))
1 [1]
2 [-2]
0 [0, 0]
1 [-1]
2 [2]
1 [1, -1]
2 [2]
0 [0, 0]
>>> for k, g in itertools.groupby(data1, key = abs):
print(k, list(g))
0 [0, 0, 0, 0]
1 [1, -1, 1, -1]
2 [-2, 2, 2]
- 返回多個迭代器itertools.tee
tee(iterable, n = 2)
- 其返回可迭代對象iterable的nge(默認爲2)迭代器
>>> import itertools
>>> for i in itertools.tee(range(10), 3): print(list(i))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
- 組合迭代器itertools.combinations和itertools.combinations_with_replacement
combinations(iterable, r)
元素不重複combinations_with_replacement(iterable, r)
元素可重複- 其返回可迭代對象iterable的元素的組合,組合的長度爲r
- 可以把它理解爲數學中的組合
>>> import itertools
>>> list(itertools.combinations([1, 2, 3], 2))
[(1, 2), (1, 3), (2, 3)]
>>> list(itertools.combinations([1, 2, 3, 4], 2))
[(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
>>> list(itertools.combinations([1, 2, 3, 4], 3))
[(1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4)]
>>>
>>>
list(itertools.combinations_with_replacement([1, 2, 3], 2))
[(1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)]
- 排列迭代器itertools.permutations
permutations(iterable, r = None)
- 排列長度爲r(默認爲序列長度)
>>> import itertools
>>> list(itertools.permutations([1, 2, 3], 2))
[(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)]
>>> list(itertools.permutations([1, 2, 3]))
[(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)]
- 笛卡爾積迭代器itertools.product
product(*iterables, repeat = 1)
- 其返回可迭代對象的元素的笛卡爾積,repeat爲可迭代對象的重複次數(默認爲1)
>>> import itertools
>>> list(itertools.product([1, 2], 'abc'))
[(1, 'a'), (1, 'b'), (1, 'c'), (2, 'a'), (2, 'b'), (2, 'c')]
>>> list(itertools.product([1, 2], repeat = 3))
[(1, 1, 1), (1, 1, 2), (1, 2, 1), (1, 2, 2), (2, 1, 1), (2, 1, 2), (2, 2, 1), (2, 2, 2)]
八:自定義類應用舉例
用戶可以通過自定義類創建和使用新的數據結構
例9.52 實現RGB顏色模型的Color類
class Color:
def __init__(self, r = 0, g = 0, b = 0):
self.__r = r
self.__g = g
self.__b = b
@property
def r(self):
return self.__r
@property
def g(self):
return self.__g
@property
def b(self):
return self.__b
def luminance(self):
#計算並返回顏色的亮度
return self.__r * .299 + .587 * self.__g + .114 * self.__b
def toGray(self):
#轉換爲灰度顏色
y = int(round(self.luminance()))
return Color(y, y, y)
def isCompatible(self, c):
#比較前景色和背景色是否匹配
return abs(self.luminance() - c.luminance()) >= 128.0
def __str__(self):
#重載方法,輸出:(r, g, b)
return '({}, {}, {})'.format(self.__r, self.__g, self.__b)
#常用顏色
WHITE = Color(255, 255, 255)
BLACK = Color(0, 0, 0)
RED = Color(255, 0, 0)
GREEN = Color(0, 255, 0)
BLUE = Color(0, 0, 255)
CYAN = Color(0, 255, 255)
MAGENTA = Color(255, 0, 255)
YELLOW = Color(255, 255, 0)
#測試代碼
if __name__ == '__main__':
c = Color(255, 200, 0)
print('顏色字符串:{0}'.format(c))
print('顏色分量:r = {0}, g = {1}, b = {1}'.format(c.r, c.g, c.b))
print('顏色亮度:{0}'.format(c.luminance()))
print('轉換爲灰度顏色:{0}'.format(c.toGray()))
print('{0} 和 {1} 是否匹配:{2}'.format(c, RED, c.isCompatible(RED)))
例9.53 實現直方圖類Histogram
import random
import math
class Stat:
def __init__(self, n):
self.__data = []
for i in range(n):
self.__data.append(0)
def addDataPoint(self, i):
"""增加數據點"""
self.__data[i] += 1
def count(self):
"""計算數據點個數之和(統計數據點個數)"""
return sum(self.__data)
def mean(self):
"""平均值"""
return sum(self.__data)/len(self.__data)
def max(self):
return max(self.__data)
def min(self):
return min(self.__data)
def draw(self):
"""繪製簡易直方圖"""
for i in self.__data:
print(' # ' * i)
if __name__ == '__main__':
st = Stat(10)
for i in range(100):
score = random.randrange(0, 10)
st.addDataPoint(math.floor(score))
print('數據點個數:{}'.format(st.count()))
print('數據點個數的平均值:{}'.format(st.mean()))
print('數據點個數的最大值:{}'.format(st.max()))
print('數據點個數的最小值:{}'.format(st.min()))
st.draw()
輸出:
數據點個數:100
數據點個數的平均值:10.0
數據點個數的最大值:14
數據點個數的最小值:7
# # # # # # # #
# # # # # # # # # #
# # # # # # # # #
# # # # # # # # # #
# # # # # # # # # #
# # # # # # # # # # #
# # # # # # # # #
# # # # # # # # # # # #
# # # # # # # # # # # # # #
# # # # # # #
填空題:2
2
>>> x = '123'
>>> print(isinstance(x, int))
False
思考題:3~11
3
在聲明派生類時,必須在其構造函數中調用基類的構造函數
>>> class Parent:
def __init__(self, param):
self.v1 = param
>>> class Child(Parent):
def __init__(self, param):
Parent.__init__(self, param)
self.v2 = param
>>> obj = Child(100)
>>> print("%d %d" % (obj.v1, obj.v2))
100 100
4
注意區分實例對象屬性和局部變量
>>> class Account:
def __init__(self, id):
self.id = id
id = 888
>>> acc = Account(100)
>>> print(acc.id)
100
5
>>> #5
>>> class Account:
def __init__(self, id, balance):
self.id = id
self.balance = balance
def deposit(self, amount):
self.balance += amount
def withdraw(self, amount):
self.balance -= amount
>>> acc1 = Account('1234', 100)
>>> acc1.deposit(500)
>>> acc1.withdraw(200)
>>> print(acc1.balance)
400
6
getattr(object, name[, default])
獲取object對象的屬性的值,如果存在則返回屬性值setattr(object, name, value)
給object對象的name屬性賦值value,如果對象原本存在給定的屬性name,則setattr會更改屬性的值爲給定的value;如果對象原本不存在屬性name,setattr會在對象中創建屬性,並賦值爲給定的value;
>>> class A:
def __init__(self, a, b, c):
self.x = a+b+c
>>> a = A(6, 2, 3)
>>> b = getattr(a, 'x')
>>> setattr(a, 'x', b+1)
>>> print(a.x)
12
7
淺拷貝,複製對象時對象中包含的子對象並不複製,而是引用同一個子對象
>>> import copy
>>> d1 = {'a' : [1, 2], 'b' : 2}
>>> d2 = copy.copy(d1)
>>> d1['a'][0] = 6
>>> sum = d1['a'][0] + d2['a'][0]
>>> print(sum)
12
8
深拷貝,可以遞歸複製對象中包含的子對象
>>> from copy import *
>>> d1 = {'a' : [1, 2], 'b' : 2}
>>> d2 = deepcopy(d1)
>>> d1['a'][0] = 6
>>> sum = d1['a'][0] + d2['a'][0]
>>> print(sum)
7
9
淺拷貝,複製對象時對象中包含的子對象並不複製,而是引用同一個子對象
>>> from copy import *
>>> list1 = [1, 2, 3]
>>> list2 = [3, 4, 5]
>>> dict1 = {"1" : list1, '2' : list2}
>>> dict2 = dict1.copy()
>>> dict1['1'][0] = 15
>>> print(dict1['1'][0] + dict2['1'][0])
30
10
深拷貝,可以遞歸複製對象中包含的子對象
>>> from copy import *
>>> list1 = [1, 2, 3]
>>> list2 = [3, 4, 5]
>>> dict1 = {"1" : list1, '2' : list2}
>>> dict2 = deepcopy(dict1)
>>> dict1['1'][0] = 15
>>> print(dict1['1'][0] + dict2['1'][0])
16
11
對象通過特殊屬性__dict__
存儲屬性,包括自定義屬性
>>> class Person:
def __init__(self, id):
self.id = id
>>> mary = Person(123)
>>> mary.__dict__['age'] = 18
>>> mary.__dict__['gender'] = 'female'
>>> print(mary.age + len(mary.__dict__))
21
>>> mary.__dict__
{'id': 123, 'age': 18, 'gender': 'female'}
上機實踐:2~3
2. 編寫程序,創建類MyMath,計算圓的周長和麪積以及球的表面積和體積,並編寫測試代碼,結果均保留兩位小數
import math
class MyMath:
def __init__(self, r):
self.r = r
def perimeter_round(self):
return 2 * math.pi * self.r
def area_round(self):
return math.pi * self.r * self.r
def area_ball(self):
return 4 * math.pi * self.r ** 2
def volume_ball(self):
return 4 / 3 * math.pi *self.r ** 3
if __name__ == '__main__':
n = float(input("請輸入半徑:"))
m = MyMath(n)
print("圓的周長 = {0:.2f}\n圓的面積 = {1:.2f}\n球的表面積 = {2:.2f}\n球的體積 = {3:.2f}".\
format(m.perimeter_round(), m.area_round(), m.area_ball(), m.volume_ball()))
輸出:
請輸入半徑:5
圓的周長 = 31.42
圓的面積 = 78.54
球的表面積 = 62.83
球的體積 = 523.60
3. 編寫程序,創建類Temperature,其包含成員變量degree(表示溫度)以及實例方法ToFahrenheit(將攝氏溫度轉換爲華氏溫度)和ToCelsius(將華氏溫度轉換爲攝氏溫度),並編寫測試代碼
class Temperature:
def __init__(self, degree):
self.degree = degree
def toFahrenheit(self):
return self.degree*9/5 + 32
def toCelsius(self):
return (self.degree -32) * 5/9
if __name__ == '__main__':
n1 = float(input("請輸入攝氏溫度:"))
t1 = Temperature(n1)
print("攝氏溫度 = {0:.2f}, 華氏溫度 = {1:.2f}".format(n1, t1.toFahrenheit()))
n2 = float(input("請輸入華氏溫度:"))
t2 = Temperature(n2)
print("攝氏溫度 = {0:.2f}, 華氏溫度 = {1:.2f}".format(t2.toCelsius(), n2))
輸出:
請輸入攝氏溫度:30
攝氏溫度 = 30.00, 華氏溫度 = 86.00
請輸入華氏溫度:70
攝氏溫度 = 21.11, 華氏溫度 = 70.00