0%

Mosh的Python课程笔记(7)--Classes

Mosh的课程网址

👉 Class: blueprint for creating new objects — Human

👉 Object: instance of a class — John, Mary, Jack

Creating Classes

1
2
3
4
5
6
7
8
9
class Point:
def draw(self):
print("draw")


point = Point()
print(type(point))

print(isinstance(point, Point)) # 判断point是不是Point的实例

输出结果:

1
2
<class '__main__.Point'>
True

Constructors

1
2
3
4
5
6
7
8
9
10
11
12
class Point:
def __init__(self, x, y): # Constructor
self.x = x
self.y = y

def draw(self):
print(f"Point ({self.x}, {self.y})")


point = Point(1, 2)
print(point.x)
point.draw()

self 指向当前 working 的 object, 比如 draw 不需要显式写 point.draw(point), 编译器会自动将当前的 object point 作为 draw() 的参数。

输出:

1
2
1
Point (1, 2)

Class vs Instance Attributes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Point:
default_color = "red" # class attribute

def __init__(self, x, y):
self.x = x # object/instance attribute
self.y = y

def draw(self):
print(f"Point ({self.x}, {self.y})")


point = Point(1, 2)
point.draw() # x和y是object attribute,每个object不同

another = Point(3, 4)
another.draw()

Point.default_color = "yellow" # 直接用类调用更改。类中所有实例共享
print(point.default_color)
print(Point.default_color)
print(another.default_color)

输出:

1
2
3
4
5
Point (1, 2)
Point (3, 4)
yellow
yellow
yellow

Class vs Instance Methods

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Point:
def __init__(self, x, y):
self.x = x
self.y = y

@classmethod
def zero(cls): # cls指向这个class本身
return cls(0, 0) # Point(0, 0)

def draw(self):
print(f"Point ({self.x}, {self.y})")


point = Point.zero() # 工厂方法:将复杂的对象构造(比如多种初始值)交给类方法去完成
point.draw() # 输出 Point (0, 0)

Magic Methods

编译器会自动调用Magic Methods。

guide book

1
2
3
4
5
6
7
8
9
10
11
12
class Point:
def __init__(self, x, y):
self.x = x
self.y = y

def draw(self):
print(f"Point ({self.x}, {self.y})")


point = Point(1, 2)
print(point)
print(str(point))

输出:

1
2
<__main__.Point object at 0x0000020E4C1A6FD0>
<__main__.Point object at 0x0000020E4C1A6FD0>

自定义magic function __str__:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Point:
def __init__(self, x, y):
self.x = x
self.y = y

def __str__(self):
return f"({self.x}, {self.y})"

def draw(self):
print(f"Point ({self.x}, {self.y})")


point = Point(1, 2)
print(point)
print(str(point))

输出:

1
2
(1, 2)
(1, 2)

Comparing Objects

1
2
3
4
5
6
7
8
9
class Point:
def __init__(self, x, y):
self.x = x
self.y = y


point = Point(1, 2)
other = Point(1, 2)
print(point == other)

输出:False。因为这样比较的是两个reference,是不同的。

方法:重定义magic functions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Point:
def __init__(self, x, y):
self.x = x
self.y = y

def __eq__(self, other):
return self.x == other.x and self.y == other.y

def __gt__(self, other):
return self.x > other.x and self.y > other.y


point = Point(10, 20)
other = Point(1, 2)
print(point == other)
print(point > other)

这时就是比较两个 object 的 x 和 y 的值了

Supporting Arithmetic Operations

1
2
3
4
5
6
7
8
9
10
11
12
13
class Point:
def __init__(self, x, y):
self.x = x
self.y = y

def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)


point = Point(10, 20)
other = Point(1, 2)
combined = point + other
print(f"Point ({combined.x}, {combined.y})")

输出:

1
Point (11, 22)

Creating Custom Containers

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class TagCloud:
def __init__(self):
self.tags = {}

def add(self, tag):
self.tags[tag.lower()] = self.tags.get(tag.lower(), 0) + 1

def __getitem__(self, tag):
return self.tags.get(tag.lower(), 0)

def __setitem__(self, tag, count):
self.tags[tag.lower()] = count

def __len__(self):
return len(self.tags)

def __iter__(self): # make it iterable
return iter(self.tags)


cloud = TagCloud()
cloud.add("Python")
cloud.add("python")
cloud.add("python")
print(cloud.tags)

print(cloud["python"]) # __getitem__实现
cloud["java"] = 10 # __setitem__实现
print(cloud.tags)

print(len(cloud)) # __len__实现

输出:

1
2
3
4
{'python': 3}
3
{'python': 3, 'java': 10}
2

Private Members

将光标放到 tags 那,F2,改成__tags,回车,所有地方都改成__tags了,这就是 private member

1
2
3
4
5
6
7
8
9
class TagCloud:
def __init__(self):
self.__tags = {}


cloud = TagCloud()
print(cloud.__dict__)
print(cloud._TagCloud__tags)
print(cloud.__tags)

输出:

1
2
3
4
5
6
{'_TagCloud__tags': {}}
{}
Traceback (most recent call last):
File "e:\HelloWorld\app.py", line 24, in <module>
print(cloud.__tags)
AttributeError: 'TagCloud' object has no attribute '__tags'

不能直接用名字调用,但是实际上python的 private member还是外部可见的,用__dict__就能查看到,用那个名字’_TagCloud__tags’也还能访问到。

Properties

Java 风格的 getter 和 setter,不 pythonic:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Product:
def __init__(self, price):
self.set_price(price)

def get_price(self):
return self.__price

def set_price(self, value):
if value < 0:
raise ValueError("Price cannot be negative.")
self.__price = value


product = Product(-50)

pythonic coding:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Product:
def __init__(self, price):
self.price = price

@property
def price(self):
return self.__price

@price.setter
def price(self, value):
if value < 0:
raise ValueError("Price cannot be negative.")
self.__price = value

# price = property(get_price, set_price)


product = Product(-10)
print(product.price)

如果将setter删掉,price 就是只读的。

Inheritance

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Animal:
def __init__(self):
self.age = 1

def eat(self):
print("eat")


# Animal: Parent, Base
# Mammal: Child, Sub
class Mammal(Animal):
def walk(self):
print("walk")


class Fish(Animal):
def swim(self):
print("swim")


m = Mammal()
m.eat()
print(m.age)

输出:

1
2
eat
1

The Object Class

所有 class 都继承于 object 类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Animal:
def __init__(self):
self.age = 1

def eat(self):
print("eat")


# Animal: Parent, Base
# Mammal: Child, Sub
class Mammal(Animal):
def walk(self):
print("walk")


m = Mammal()
print(isinstance(m, Animal)) # True
print(isinstance(m, Mammal)) # True
print(isinstance(m, object)) # True
print(issubclass(Mammal, Animal)) # True
print(issubclass(Mammal, object)) # True

Method Overriding

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Animal:
def __init__(self):
print("Animal Constructor")
self.age = 1

def eat(self):
print("eat")


# Animal: Parent, Base
# Mammal: Child, Sub
class Mammal(Animal):
def __init__(self):
super().__init__() # 调用父类的__init__(),否则会被子类的覆盖掉而不执行
print("Mammal Constructor")
self.weight = 2

def walk(self):
print("walk")


m = Mammal()
print(m.age)
print(m.weight)

输出:

1
2
3
4
Animal Constructor
Mammal Constructor
1
2

Multi-level Inheritance

Abuse inheritance:

​ Employee - Person - LivingCreature - Thing

make software complex

Multiple Inheritance

Abuse case:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Employee:
def greet(self):
print("Employee Greet")


class Person:
def greet(self):
print("Person Greet")


class Manager(Employee, Person):
pass


manager = Manager()
manager.greet()

将 class Manager(Employee, Person) 中的父类换个顺序就有完全不同的输出:

(Employee, Person) 输出: Employee Greet

(Person, Employee) 输出: Person Greet

Good Example

1
2
3
4
5
6
7
8
9
10
11
12
class Flyer:
def fly(self):
pass


class Swimmer:
def swim(self):
pass


class FlyingFish(Flyer, Swimmer):
pass

子类的两个父类应该是没有什么共同点的,而子类兼具两个父类的method。

A Good Example of Inheritance

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class InvalidOperationError(Exception):
pass


class Stream:
def __init__(self):
self.opened = False

def open(self):
if self.opened:
raise InvalidOperationError("Stream is already opened.")
self.opened = True

def close(self):
if not self.opened:
raise InvalidOperationError("Stream is already closed.")
self.opened = False


class FileStream(Stream):
def read(self):
print("Reading data from a file.")


class NetworkStream(Stream):
def read(self):
print("Reading data from a network.")

Abstract Base Classes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
from abc import ABC, abstractmethod


class InvalidOperationError(Exception):
pass


class Stream(ABC): # 继承自ABC(抽象类简写)
def __init__(self):
self.opened = False

def open(self):
if self.opened:
raise InvalidOperationError("Stream is already opened.")
self.opened = True

def close(self):
if not self.opened:
raise InvalidOperationError("Stream is already closed.")
self.opened = False

@abstractmethod
def read(self): # 抽象方法
pass


class FileStream(Stream):
def read(self):
print("Reading data from a file.")


class NetworkStream(Stream):
def read(self):
print("Reading data from a network.")


class MemoryStream(Stream):
def read(self):
print("Reading data from the memory.")


stream = MemoryStream()
stream.read()

👉 Stream 不能创建实例

👉 Stream 的子类必须实现 read() ,或者也是抽象类

Polymorphism

Many Forms

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from abc import ABC, abstractmethod


class UIControl(ABC):
@abstractmethod
def draw(self):
pass


class TextBox(UIControl):
def draw(self):
print("TextBox")


class DropDownList(UIControl):
def draw(self):
print("DropDownList")


def draw(controls): # 在run-time才知道control具体是什么
for control in controls:
control.draw()


ddl = DropDownList()
textbox = TextBox()
draw([ddl, textbox]) # 多种对象实现一种方法

输出:

1
2
DropDownList
TextBox

Duck Typing

If it walks and quacks like a duck, it is a duck.

上例中并不需要抽象类UIControl。只要类中实现了draw()方法,python就认为是个UIControl,就能执行draw(controls)方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from abc import ABC, abstractmethod


class TextBox:
def draw(self):
print("TextBox")


class DropDownList:
def draw(self):
print("DropDownList")


def draw(controls):
for control in controls:
control.draw()


ddl = DropDownList()
textbox = TextBox()
draw([ddl, textbox])

Extending Built-in Types

Demo 1: 扩展str类

1
2
3
4
5
6
7
8
class Text(str):
def duplicate(self):
return self + self


text = Text("Python")
print(text.duplicate()) # 本类方法
print(text.lower()) # 父类方法

输出:

PythonPython

python

Demo 2: 扩展list类

1
2
3
4
5
6
7
8
class TrackableList(list):
def append(self, object):
print("Append called")
super().append(object) # 调用父类方法


list = TrackableList()
list.append("1")

输出:

Append called

Data Classes

对于一个只有data而没有方法的类:

1
2
3
4
5
6
7
8
9
10
11
12
class Point:
def __init__(self, x, y):
self.x = x
self.y = y

def __eq__(self, other) -> bool:
return self.x == other.x and self.y == other.y


p1 = Point(1, 2)
p2 = Point(1, 2)
print(p1 == p2)

可以用 namedtuple make code cleaner:

1
2
3
4
5
6
7
8
9
10
from collections import namedtuple

Point = namedtuple("Point", ["x", "y"])
p1 = Point(x=1, y=2) # must be keyword arguments
p2 = Point(x=1, y=2)

print(p1 == p2) # True
print(p1.x) # 像类一样调用:1

p1.x = 10 # AttributeError: can't set attribute

只能新建一个namedtuple,用p1指向:

1
p1 = Point(x=10, y=2)