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))
|
输出结果:
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): 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() 的参数。
输出:
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"
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) point.draw()
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): return cls(0, 0)
def draw(self): print(f"Point ({self.x}, {self.y})")
point = Point.zero() point.draw()
|
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))
|
输出:
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})")
|
输出:
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): return iter(self.tags)
cloud = TagCloud() cloud.add("Python") cloud.add("python") cloud.add("python") print(cloud.tags)
print(cloud["python"]) cloud["java"] = 10 print(cloud.tags)
print(len(cloud))
|
输出:
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
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")
class Mammal(Animal): def walk(self): print("walk")
class Fish(Animal): def swim(self): print("swim")
m = Mammal() m.eat() print(m.age)
|
输出:
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")
class Mammal(Animal): def walk(self): print("walk")
m = Mammal() print(isinstance(m, Animal)) print(isinstance(m, Mammal)) print(isinstance(m, object)) print(issubclass(Mammal, Animal)) print(issubclass(Mammal, object))
|
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")
class Mammal(Animal): def __init__(self): super().__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): 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): for control in controls: control.draw()
ddl = DropDownList() textbox = TextBox() draw([ddl, 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) p2 = Point(x=1, y=2)
print(p1 == p2) print(p1.x)
p1.x = 10
|
只能新建一个namedtuple,用p1指向: