面向过程

专注于实现功能的每一个步骤。

例如给定事件:洗衣服

put(detergent, washing_machine)
put(clothes, washing_machine)
start(washing_machine)
wash(washing_machine, clothes)
dry(washing_machine, clothes)

是编年体

编年体是一种按照年代顺序编写的历史记载形式。它通常按照年份或历代统治者的顺序,按照时间线的方式来叙述历史事件。编年体的作品通常简洁明了,注重事实的记录,不过多描写人物的思想和情感。编年体的代表作品有《史记》和《汉书》等。

面向对象

理解概念

class human:
def put(self, be_put, to):
...
def start(self, machine):
...
class washing_machine:
def wash(self, be_washed):
...
def dry(self, be_dried):
...

是纪传体

纪传体是一种以人物为中心,按照人物的生平经历来叙述历史的文学体裁。它通常以人物的事迹为主线,通过描写人物的行为、性格、思想等方面来反映历史背景和社会风貌。纪传体的作品通常较为生动具体,注重人物形象的塑造和故事情节的展开。纪传体的代表作品有《史记》中的列传部分和《资治通鉴》等。

面向对象可以让逻辑更加清晰,信息更集中。

特性

  • 封装
  • 继承
  • 多态

定义

Pascal命名法

每个单词的首字母大写,适用于名。eg: UserAccount, CustomerOrder, PaymentData

underscore下划线命名法

下划线分割单词,适用于变量名。eg: user_name, total_distance, list_count

定义属性

对象有什么性质?

class CuteCat: # define a class named CuteCat
def __init__(self):
'''
构造函数,构造方法,用于定义实例对象的属性,名称固定,且第一个参数的值固定是self
'''
self.name = "Lambton" # 实例对象的参数

调用类名称创建实例对象时,__init__()函数会自动执行,对name属性赋值

但是显然并不是所有的可爱猫猫都是一个名字,所以应该进行一个参数的传入来赋值

class CuteCat:
def __init__(self, cat_name):
self.name = cat_name
cat1 = CuteCat("Jojo")
print(cat1.name) # 会输出Jojo

定义方法

对象能做什么事情?

与普通的函数类似,但有两个区别:

  1. 写在class的里面,注意缩进
  2. 第一个参数被占用,为self。可以让我们在方法里面去获取和修改对象的属性,比如可爱猫猫的叫唤次数和年龄成正比

实践

定义一个学生类

  1. 属性包括学生姓名、学号、以及语数英三科的成绩
  2. 能够设置学生某科目的成绩
  3. 能够打印出该学生所有科目的成绩
class Student:
def __init__(self, name, student_id):
self.name = name # self.name是绑定在对象上的属性,而name只是一个传参数的变量
self.id = student_id
self.grades = {"chinese": 0, "math": 0, "english": 0} # 定义一个字典来记录成绩

def set_grade(self, course, grade):
if course in self.grades:
self.grades[course] = grade
'''
如果在字典的键里面有这门科目,就修改成绩
'''

def print_grades(self):
print(f"Student{self.name} (id:{self.id}) 's grades are:")
for course in self.grades: # grades这个字典里面,循环它的键
print(f"{course}: {self.grades[course]}") # 通过grades字典的键来取得值


chen = Student("chen", 1)
shi = Student("shi", 2)
chen.set_grade("chinese", 144)
chen.print_grades()

类的继承

DRY原则:Don’t Repeat Yourself

部分类有相同的属性,避免再用一遍,直接从父类继承过来即可

子类会继承父类的所有方法,包括构造方法__init__()。如果不想用父类的方法,只需要在子类中重新定义同名方法即可,在调用时会优先查看子类中是否有该方法,如果没有的话再去父类看。

一个问题就是:__init__()构造方法,子类和父类并不完全一致,应该如何处理?

class Cat(Mammal):
def __init__(self, name, sex):
super().__init__(name, sex)
self.has_tail = True

这段代码让猫猫继承了名字和性别属性从Mammal父类,并且定义了一个新的属性has_tail

实践

# 类继承练习:人力系统
# - 员工分为两类:全职员工 FullTimeEmployee、兼职员工 PartTimeEmployee。
# - 全职和兼职都有"姓名 name"、"工号 id"属性,
# 都具备"打印信息 print_info"(打印姓名、工号)方法。
# - 全职有"月薪 monthly_salary"属性,
# 兼职有"日薪 daily_salary"属性、"每月工作天数 work_days"的属性。
# - 全职和兼职都有"计算月薪 calculate_monthly_pay"的方法,但具体计算过程不一样。

class Employee:
def __init__(self, name, employee_id):
self.name = name
self.id = employee_id

def print_info(self):
print(f"name: {self.name}, id: {self.id}")


class FullTimeEmployee(Employee):
def __init__(self, name, employee_id, month_salary):
super().__init__(name, employee_id)
self.month_salary = month_salary

def calculate_monthly_pay(self):
return self.month_salary


class PartTimeEmployee(Employee):
def __init__(self, name, employee_id, daily_salary, work_days):
super().__init__(name, employee_id)
self.daily_salary = daily_salary
self.work_days = work_days

def calculate_monthly_pay(self):
return self.daily_salary * self.work_days


zhangsan = FullTimeEmployee("张三", "1001", 6000)
lisi = PartTimeEmployee("李四", "1002", 230, 15)
zhangsan.print_info()
lisi.print_info()
print(zhangsan.calculate_monthly_pay())
print(lisi.calculate_monthly_pay())

如果父类的属性,子类没有的话,可以给父类的init函数的一些变量一个初始值

class Employee:
def __init__(self, name, employee_id=None):
self.name = name
self.id = employee_id

这样在继承的时候,可以直接写成

class PartTimeEmployee(Employee):
def __init__(self, name, daily_salary, work_days):
super().__init__(name) # 只使用父类的name属性,不用id属性
self.daily_salary = daily_salary
self.work_days = work_days