Python基础学习笔记(3)模块 对象 类
前言
模块 对象和类的学习
一、引入模块
但写代码的时候把别人写好的代码直接拿过来用,是非常正常并且高效的行为。比如如果你要对列表里面的所有数字求和,你可以自个写个函数,然后每次需要求和时就调用这个函数。但 Python 已经给你提供了一个叫 SUM 的内置函数,直接调用就可以得到列表里所有数字的和,就没必要自己定义。
比如 Python 有个模块叫statistics,帮助进行统计相关的计算里面就有个叫做 Median 的用于计算中位数的函数,那么这一坨代码就可以被简化成两行代码,第一行引入 statistics 模块,第二行使用 statistics 模块里面的 Median 函数。
import statistics
print(statistics.median([1,2,3,4,5,6,7]))#求中位数,import后的模块在使用时用模块名.函数名(参数)来调用
4
如果想看模块中的函数具体怎么写的,在Windows用Ctrl+左键点击函数名,在Mac上用Command+左键点击函数名
具体引入的方法就是在 import 后面跟上模块的名字,比如 import statistics 之后要用模块里的函数或变量的时候,就用模块名.函数名或模块名点.变量名来使用。
二、Python面向对象编程 封装、继承、多态.
1.什么是面向对象
面向对象编程首先并不会聚焦于第一步是做什么,而是模拟真实世界,先考虑各个对象有什么性质,能做什么事情。
比如我们去银行ATM取钱,每个 ATM 都有自己的性质,包括编号、银行支行,那么我们可以提取出这些性质定义 ATM 类,然后用类创建对象。类和对象之间的关系是类是创建对象的模板,对象是类的实例。更通俗点说,可以把类想象成制造具体对象的图纸,图纸上说 ATM 可以有编号、银行、支行这三个属性。那么通过 ATM 类创建出来的 ATM 实例,无论是招商银行南园支行编号 001 的ATM,还是中国银行北园支行编号 002 的 ATM 都是 ATM 对象,些编号银行支行则是不同 ATM 对象各自的属性。我们可以用相同的方式把纸币类以及通过纸币类创建的两个纸币对象也定义出来,现在这些对象就可以直接作为参数传入存钱和取钱函数中。
class ATM:
def __init__(self,atm_id,bank_name,branch_name):#self是指向对象本身的指针,是一个约定俗成的名字,可以改成其他名字,但是不推荐,__init__是一个特殊的方法,用来初始化对象,也叫构造方法,当创建对象时,会自动调用这个方法。
self.atm_id=atm_id
self.bank_name=bank_name
self.branch_name=branch_name
#创建一个对象
atm1=ATM('001','招商银行','南苑支行')
print(atm1.atm_id)
001
除了属性之外,另一个能和对象绑定的是方法。正如真实世界里,对象拥有不同的属性,并且能做不同的事情,那属性就对应对象拥有的性质,而方法对应对象能做些什么?比如洗衣服,人能放东西和开机,洗衣机能清洗和烘干,所以这些可以作为类的方法被定义出来。换个角度来看,所谓方法就是放在类里面的函数,所谓属性就是放在类里面的变量。定义好类之后就可以通过类来创建对象,然后让各个对象去执行这些方法。
封装,表示写类的人将内部实现细节隐藏起来。你其实只需要知道它有什么方法,有什么作用,具体怎么用就足够了,不需要知道方法里面具体是怎么写的,这和真实世界是一样的,我们现实中使用洗衣机时也是直接通过操作界面的按钮来使用,不需要知道里面的原理,也不需要钻到洗衣机背后研究电路板,封装能减少我们对不必要细节的精力投入。
继承就像现实中的儿子继承爸爸,爸爸继承爷爷类,也可以由子类和父类来表示从属关系,比如小学生、大学生都是学生,都应该有学号,年级的属性,都要去学校。可以看到这两个类之间的共同之处,导致有很多重复代码产生,但我们可以创建出一个叫学生的父类,然后让小学生和大学生去继承这个类。这样做的好处是父类的那些属性方法都可以被继承,不需要反复定义
2.如何创建类
定义一个类,python在定义类名的时候用的是Pascal命名法,也就是每个单词的首字母大写,比如ATM,Student,UserAccount等,后面要跟一个冒号,然后是类的属性和方法。
举个例子,我们可以定义一个可爱猫猫类叫CuteCat(这里类名首字母要大写来区分单词),下一步是思考这个类的对象都拥有什么属性,猫猫可以有的属性包括名字、年龄、花色等。类有一个特殊的方法叫做构造函数,主要作用是定义实例对象的属性。它必须要被命名为init,前后得有两个下划线,注意是俩括号里面可以放任意数量的参数,但第一个参数永远是被占用的,得用于表示对象自身,约定俗成叫self,它能帮你把属性的值绑定在实例对象上,比如猫猫的名字属性为小花,可以写self.name=小花,说明是对象的 name 属性的值。如果写成 name =小花,前面没有 self ,Python会觉得只是在给普通的 name 变量赋值,就不会把这个值看成是对象的属性。
class CuteCat:#每个单词的首字母大写
def __init__(self,cat_name,cat_age,cat_color):
#千万注意 init是用的双下划线,记住是双下划线
self.name=cat_name
self.age=cat_age
self.color=cat_color#对象的属性
cat1=CuteCat('小花',3,'白色')#创建一个对象
print(cat1.age)
下面是定义对象拥有的方法,表示对象能做什么事情,比如可爱猫对象可以思考,可以叫唤。定义方法很简单,和我们创建普通的函数差不多,只有两个区别,第一是要写在 class 里面,前面要有缩进来表示这是属于该类的方法。第二是和init一样第一个参数被占用,用于表示对象自身约定,俗称为self。对于init,我们说 self 可以让我们把属性值绑定到对象自身上,那方法为什么也需要这个self?一个作用是可以让我们在方法里面去获取或修改和对象绑定的属性,比如如果小猫叫唤的次数和年龄成正比的话,就可以这样写,字符串乘以数字表示把字符串重复那么多次,喵乘以对象的年龄属性,结果就是如果小猫一岁会打印出喵,小猫 3 岁会打印出喵喵喵,这样就实现了方法调用,结果根据属性的不同而改变。
def speak(self):
print('喵喵喵'*self.age)#上
下面是例子,
定义一个学生类,要求:
1.属性包括学生姓名、学号,以及语数英三科的成绩
2.能够设置学生某科目的成绩
3.能够打印出该学生的所有科目成绩
class Student:#定义一个学生类
def __init__(self,name,student_id):#初始化方法,初始化学生的姓名和学号
self.name=name
self.student_id=student_id
self.grades={'语文':0,'数学':0,'英语':0}#这是一个字典,字典中的键是科目,值是成绩,初始化成绩都是0
def set_grade(self,subject,grade):#定义一个方法,设置学生某科目的成绩
if subject in self.grades:
self.grades[subject]=grade# 如果科目在字典中,就把成绩赋值给这个科目,grades[subject]是字典中的键,grade是值
def print_grade(self):#定义一个打印的方法
print(f'学生{self.name}学号{self.student_id}成绩是:' )
for subject in self.grades:
print(f'{subject}:{self.grades[subject]}')#打印学生的所有科目成绩
chen=Student('chen','001')#创建一个学生对象
zhang=Student('zhang','002')#创建一个学生对象
chen.set_grade('语文',90)#设置chen的语文成绩是90
zhang.set_grade('数学',80)#设置zhang的数学成绩是80
chen.print_grade()#打印chen的所有成绩
学生chen学号001成绩是:
语文:90
数学:0
英语:0
3.类的继承
在面向对象的世界里,如果把人类和小猫都抽象成类的话,可以写成这样,人类有名字、性别,有两只眼睛、没有尾巴,这些都是属性。然后人类可以呼吸,拉屎,阅读。小猫也有名字、性别,两只眼睛有尾巴。然后小猫也可以呼吸拉屎,虽然不能阅读,但是可以在你阅读的时候抓你沙发。
def __init__(self,name,sex):
self.name=name
self.sex = sex
self.num_eyes=2#两只眼睛
self.has_tail = False#没有尾巴
def breathe(self):
print(self.name+"在呼吸..")
def poop(self):
print(self.name+"在拉屎.")
def read(self):
print(self.name+"在阅读..")
子类会继承父类的属性和方法。人类和猫都是哺入类动物,它们都有两只眼睛,都会呼吸和拉屎,因此可以去创建一个叫做 mammal 哺乳类动物的类,把这些共享的属性和方法全部都挪进去,然后人类和猫都可以继承这个类。具体写法是在类名后面加上括号里面写上父类的名字。这个时候这个构造函数以及拉屎和呼吸的两个方法虽然没有在 human 和 cat 类下面,但也被继承了。现在如果创建一个 cat 实例,由于子类没有自己的构造函数,就会调用到父类的构造函数,让实例具备名字、性别、眼睛数等属性。这个实例调用拉屎方法时用的也会是父类的方法。但是如果此类有自己的拉屎的方法的话,就会调用自己的,这背后的逻辑是优先看所属的类有没有该方法,没有的话网上找父类的同名方法用。
class Mammal:
def __init__(self,name,sex):
self.name=name
self.sex = sex
self.num_eyes =2
def breathe(self):
print (self.name+'在呼吸')
def poop(self):
print(self.name+"在拉屎..")
class Human(Mammal):
def read(self):
print(self.name +"在阅读..")
class Cat(Mammal):
def scratch_sofa(self):
print(self.name+"在抓沙发.")
现在如果创建一个 cat 实例,由于子类没有自己的构造函数,就会调用到父类的构造函数,让实例具备名字、性别、眼睛数等属性。这个实例调用拉屎方法时用的也会是父类的方法。但是如果此类有自己的拉屎的方法的话,就会调用自己的,这背后的逻辑是优先看所属的类有没有该方法,没有的话往上找父类的同名方法用。
如果是下面这种因为 人和cat的has_tail属性不一样,不能只写在父类里。但如果我们给子类写init的方法,只def has_tail的话,那创建子类实例时就会优先调用子类的构造函数,导致实例只有 has_tail属性,所以我们能就只能在子类 init 里面把name、 sex 等属性也全部都写上,但这又会造成重复代码。
class Human(Mammal):
def __init__(self,name,sex):
self.name= name
self.sex = sex
self.num_eyes =2
self.has_tail = False #没有尾巴
class cat(Mammal):
def __init__(self,name,sex):
self.name= name
self.sex = sex
self.num_eyes =2
self.has_tail = True #有尾巴
更优雅的做法是在子类下面用 super 这个方法, super 会返回当前类的父类,所以在子类的 init 方法里面写 super init。这就会调用父类的构造函数,那么子类也会有姓名、性别眼睛数量的属性了。所以通过继承,我们成功把重复的属性和方法代码行给移除了,并且在实例化获得属性调用方法是和之前都一毛一样,没有任何区别。
class Human(Mammal):
def __init__(self,name,sex):
super().__init__(name,sex)
self.has_tail = False #没有尾巴
class cat(Mammal):
def __init__(self,name,sex):
super().__init__(name,sex)
self.has_tail = True #有尾巴
那么什么时候可以用继承,如果说 a 和 b 两个东西,你可以说成 a 是b,那么就可以把 a 写成是 b 的子类。比如说人类是动物,那人类可以写成是动物的子类。新能源车是车,那新能源车可以写成是车的子类。
实验下代码
类继承练习:人力系统
-员工分为两类:全职员工FullTimeEmpLoyee、兼职员工PartTimeEmployee。
-全职和兼职都有"姓名name"、"工号id"属性,
-都具备"打印信息print_info”(打印姓名、工号)方法。
-全职有"月薪monthLy_saLary"属性,
-兼职有"日薪daily_saLary"属性、“每月工作天数work_days"的属性。
-全职和兼职都有"计算月薪calculate_monthly_pay"的方法,但具体计算过程不一样。
class Employee:
def __init__(self,name,id):#初始化方法,初始姓名和工号
self.name=name#姓名
self.id=id#工号
def print_info(self):#打印信息方法
print(f'姓名:{self.name} 工号:{self.id}')#打印姓名和工号
class FullTimeEmployee(Employee):#全职员工类继承员工类
def __init__(self,name,id,monthly_salary):#初始化方法,初始姓名,工号,月薪
super().__init__(name,id)#调用父类的初始化方法
self.monthly_salary=monthly_salary#月薪
def calculate_monthly_pay(self):#计算月薪方法
return self.monthly_salary#返回月薪
class PartTimeEmployee(Employee):#兼职员工类继承员工类
def __init__(self,name,id,daily_salary,work_days):#初始化方法,初始姓名,工号,日薪,工作天数
super().__init__(name,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('张三','001',10000)#姓名是张三,工号是001,月薪是10000
zhangsan.print_info()#打印张三的信息
#创建一个兼职员工对象
lisi=PartTimeEmployee('李四','002',200,20)#姓名是李四,工号是002,日薪是200,工作天数是20
lisi.print_info()#打印李四的信息
print(zhangsan.calculate_monthly_pay())#打印张三的月薪
print(lisi.calculate_monthly_pay())#打印李四的月薪
姓名:张三 工号:001
姓名:李四 工号:002
10000
4000
总结
`
笔记分为三个部分:模块的使用、面向对象编程基础以及类的创建和继承。
1.模块:介绍了如何在Python中引入和使用模块,例如使用内置的sum函数和statistics模块中的median函数。说明了查看模块内部函数的具体实现方法,以及通过import关键字引入模块的语法。
2.面向对象编程:解释了面向对象编程(OOP)的基本概念,包括封装、继承和多态。通过ATM和猫的例子说明了类和对象的关系,以及如何定义属性和方法。强 调了封装在隐藏实现细节方面的作用,以及继承在避免代码重复方面的优势。
3.类的创建和继承:详细说明了如何定义一个类,包括类名的命名规则、构造函数__init__的用法以及如何在类中定义方法。通过学生类和哺乳动物类的例子,展示了如何创建具有特定属性和方法的类,以及如何实现类的继承。最后,给出了一个员工系统的小案例,演示了全职员工和兼职员工类的定义和继承。