1 三大编程范式
1.1 面向过程(如linux内核):将一个大的功能分成若干小功能去从上到下依次处理,即我们最开始的编程思路
1.2 函数式编程:三大特点
1)无变量的赋值
2)不使用for、while等循环,所有循环都用递归实现,如尾调用优化
3)函数的传入参数为函数OR函数的返回值为函数,具体参考函数那章
#函数式编程实现计算阶乘功能
def sum_new(x) : if x == 1 : return x return x*sum_new(x-1) print(sum_new(5))
1.3 面向对象
优点:结构化的编程,可扩展性强。
#类:具有相似功能和属性的事物一个集合,可以生成对象,或者说,将具有相同数据属性和函数属性的一类事物封装到一起
注意:类和对象都用点来访问自己的属性
#对象:基于类创建的具有具体功能和属性的一个事物
#实例化:由类生产对象的过程叫做实例化(实例=对象)
1.3.1面向对象设计(OOD)
#利用函数,将对象的属性和功能整合到一起
def type_human(name,gender,age) : #定义一个类 def func_eat(object_human) : #类中的具体对象的功能 print('%d岁的%s人%s在吃饭' %(object_human['age'],object_human['gender'],object_human['name'])) def func_run(object_human) : #类中的具体对象的功能 print('%d岁的%s人%s在跑步' %(object_human['age'],object_human['gender'],object_human['name'])) def init(name,gender,age) : #初始化具体对象,这里包括了对象的属性和功能 object_human={ 'name':name, 'gender':gender, 'age':age, 'func_eat':func_eat, 'func_run':func_run } return object_human return init(name,gender,age) object1 = type_human('alex','男',18) #得到一个具体对象 object1['func_run'](object1) #执行对象的具体功能
1.3.2 面向对象编程(object-oriented programming)
#使用 class语法来进行面向对象设计
#声明类与实例化
#python3中都为新式类(就算class a : 这样,不直接写出继承object类,也相当于默认给你变成 class a(object):),python2中分为经典类和新式类(class a(object) :的形式,即继承了object类)
#注意继承的父类为什么类,就是什么类
#经典类
class Human : #类名,按约定俗成来首字母要大写
pass #这里写类体(主体)
a = Human() #实例化,产生一个对象
print(Human,a,sep='\n') #
#<__main__.Human object at 0x0143ED90>
#新式类
class Human(object): #这里的object 叫做继承
#类的属性
数据属性:就是变量
class Human: character = '两条腿走路' #定义类的数据属性 print(Human.character) #获得类的数据属性,即变量值
函数属性:就是函数,在面向函数里称为方法
#调用类的属性、__dict__方法
class Human:
character = '两条腿走路' #数据属性
def run(): #定义函数属性,()里可以不传参数
print('跑步中')
def jump(self): #函数属性,标准写法,传参数
print('%s跳了起来' %self)
print(Human.character) #获得类的数据属性,即变量值
Human.run() #执行函数属性
Human.jump('alex')
print(dir(Human)) #查看类的属性(只有属性的名字),__XXX__之类的是系统自带的属性,自己定义从后往前看
print(Human.__dict__) #查看类的属性字典(有名字,有内容),可以被取出调用
#也可以用属性字典调用类的属性
print(Human.__dict__['character'])
Human.__dict__['run']()
Human.__dict__['jump']('alex')
#一些特殊的内置类属性
class Human: '人类' #类的描述信息,跟函数里的一样 character = '两条腿走路' def run(self): print('%s在跑步中' %self) def jump(self): print('%s跳了起来' %self) print(Human.__name__) #类名称 print(Human.__doc__) #类的描述信息 print(Human.__base__) #类的祖先print(Human.__bases__) #元组的形式表示类的祖先 print(Human.__module__) #显示这个类模块所属的位置 print(Human.__class__) #
dir() 是 Python 提供的一个 API 函数,dir() 函数会自动寻找一个对象的所有属性,包括搜索 __dict__ 中列出的属性!!!!
#实例化
class Human: '人类' res = '这是类属性' #__init__函数里定义的是实例属性,即对象特有的属性 def __init__(self,name,age,gender): #给了类具体的数据属性,实例化出来一个对象,__init__(self,name,age,gender)中的self必须加 # 并且与前面用函数的面向对象设计来说,自动帮你传参数,自动帮你返回值 self.name = name #所以的self都代表实例本身,如下文的 对象a self.age = age self.gender = gender def run(self): #对象的函数属性 print('%s在跑步中' %self.name) def jump(self): print('%s跳了起来' %self.name) a = Human('alex',18,'male') #实例化得到一个对象a print(a,a.__dict__,sep='\n') #<__main__.Human object at 0x00EFED90> --> 一个对象 #{'name': 'alex', 'age': 18, 'gender': 'male'}--> 实例属性字典__dict__ Human.run(a) #把对象带入类的功能(太low,用下面的新方法) #a对象原本只能调用 __init__ 函数作用域下的属性,但如果在此作用域下无相应属性,则会到上一层作用域去找,例如以下: a.jump() a.run() print(a.res)
#注意,实例对象没有函数属性(但我感觉也是可以给实例对象定义函数属性,但意义未知,如下为我的思路)
class Human: '人类' res = '这是类属性' def __init__(self,name,age,gender,func): self.name = name self.age = age self.gender = gender self.func = func(name,age,gender) def run(self): print('%s在跑步中' %self.name) def jump(self): print('%s跳了起来' %self.name) def func(name,age,gender): print('%s的%s(%s)在模拟实例对象的函数属性' %(age,name,gender)) a = Human('alex',18,'male',func) a.func
#类属性的查、增、删、改
class Human: country = 'Chinese' def __init__(self,name): self.name = name def play_games(self): pass a = Human('alex') #类的数据属性 print(Human.country) #查看 Human.country = 'American' #修改 Human.new_type = '新增的类属性' #增加 del Human.new_type #删除 #类的函数属性 def new_func(self) : print('这是新增函数属性') Human.new_func = new_func #增加 a.new_func() #相当于Human.new_func(a)
#实例属性的查、增、删、改
class Human: country = 'Chinese' def __init__(self,name): self.name = name def play_games(self): pass a = Human('alex') a.age = 18 #增加 a.name = 'Elizabeth' #修改 print(a.name) #查看 del a.age #删除 def new(self): #不要往实例里加函数属性,没意义 print('这是实例的函数属性') a.new = new a.new(a)
#注意!!!!!!!
可以通过 a.__dict__['age'] = 18 这种方式来增、删、改 实例属性或类属性,但最好不要这么做,可能会导致不稳定
#定义一个类,只当做一个作用域去用,类似于C语言中的结构体
class scope : x = '作用域内的局部变量' x='全局变量' print(scope.x,x)
#几个小补充
class scope : country = 'China' def __init__(self,name): self.name = name p1 = scope('alex') p1.country = 'America' #相当于给实例对象添加了一个新变量属性,而不是返回上一级修改类的属性 print(scope.country,p1.country)
#在类体中,若用类的方式去调用的变量不存在可以往上一层找,但不能超出类的作用域
country = 'China' class scope : def __init__(self,name): self.name = name p1 = scope('alex') print(p1.country) #虽然外面的全局变量有,但是找不到,会报错
#以上的前提是使用.的方式调用变量,如果只是普通调用变量,则如下
country = 'China' class scope : country = 'America' #不是通过类调用的country,所以下面打印的并不是America def __init__(self,name): self.name = name print(country) #China 非通过.的方式调用,就直接找类外的值 p1 = scope('alex')
#可以在__inti__函数里加入输入输出语句,但是不要这么做(会先显得你很初级,因为指定函数都是指向特定功能的,不同功能之间应该用不同函数区分开)
class scope : def __init__(self): name = input('输入名字') self.name = name p1 = scope() print(p1.name)
#面向对象定义出的类属性和实例属性其实与现实中的分类还是有点区别的,是要对写程序有指定意义的
2 静态属性
#其实就是把函数属性的调用伪装成一个数据属性,称它为静态属性(好处就是把背后的逻辑给隐藏起来)
class Residence : def __init__(self,name,length,width): self.name=name self.length=length self.width=width @property #一个内置的在类里使用的property函数(装饰器) # 效果是隐藏函数的调用(让cal_area()这种调用方式变成cal_area ),即将类的函数属性变为数据属性 def cal_area(self): return self.width*self.length p1 = Residence('my home',100,100) print(p1.cal_area) #原本应该是 p1.cal_area() 这样调用才对,因为加了property装饰器才这样
3 类方法
#通常我们要调用类的函数属性时,需要先实例化一个对象,然后用 类.函数属性(对象) 例如 Human.func(p1)来调用
#类方法则给我们提供了一个将其与对象分离的方法,即不需要实例化对象,也能调用类的函数属性
class Residence : value = 'human' def __init__(self,name,length,width): self.name=name self.length=length self.width=width @classmethod #用于分离类和对象的装饰器 def sleep(cls,x): #这里cls特指类名 return cls.value,x print(Residence.sleep(10)) p1 = Residence('alex',10,10) print(p1.sleep('这样居然也可以!!!')) #并没有什么意义,因为对象的属性并不能在这个函数中被调用(调用不到,会报错)
4 静态方法
# staticmethod也叫做类的工具包,不跟类绑定,也不跟实例绑定
#使用它,函数将不属于类也不属于实例,即没法通过类或者实例对象传值,比如原本的self传对象,或者上文的cls传的类
#但还在类的字典里
class Residence : value = 'human' def __init__(self,name,length,width): self.name=name self.length=length self.width=width @staticmethod def text(x,y): #也可以不传参数 return x,y print(Residence.__dict__) p1 = Residence('alex',10,10) print(p1.text(1,2))
5 组合
#将一个大类分成几个小类再组合到一起
#当类和类之间有关联,但是没有共同点时,可以使用组合(比如学校和老师)
#组合的前身,一种low的方法实现
class School : def __init__(self,name,addr): self.name=name self.addr=addr self.course_list=[] def teaching(self): print('%s在%s教学' %(self.name,self.addr)) class Course : def __init__(self,course_name,price,period): self.course=course_name self.price=price self.period=period def func(self): pass s1=School('ouc','qingdao') p1=Course('physic',0,'0.5y') s1.course_list.append(p1) print(s1.course_list[0].course)
#真正的组合
class Course : def __init__(self,major,price,period,teacher): self.major=major self.price=price self.period=period self.Teacher=teacher def func(self): pass class Teacher : def __init__(self,teacher_name,age,gender): self.teacher_name=teacher_name self.age=age self.gender=gender def teaching(self): print('%s的%s老师%s在教学' % (self.age,self.gender,self.teacher_name,)) class School : def __init__(self,name,addr,course): self.name=name self.addr=addr self.Course=course def teaching(self): print('%s的%s老师%s在%s教%s课' % (self.Course.Teacher.age,self.Course.Teacher.gender, self.Course.Teacher.teacher_name,self.name,self.Course.major)) t1=Teacher('alex',18,'男') c1=Course('physic',0,'0.5y',t1) s1=School('ouc','qingdao',c1) print(s1.Course.major,s1.Course.Teacher.teacher_name) s1.Course.Teacher.teaching() s1.teaching()
6 面向对象的三大特性
6.1 继承
#继承的总体结构
class Parentclass1 : pass class Parentclass2 : pass class subclass1(Parentclass1) : #单继承 pass class subclass2(Parentclass1,Parentclass2) : #多继承
#子类继承了父类的属性
class Dad : money = 1000 def __init__(self,name): self.name=name def work(self): print('%s在工作中' %(self.name)) class Son(Dad) : pass print(Son.__dict__) #从父类继承的属性并不在里面 print(Son.money) #但可以调用父类的数据属性 d1=Son('father') #也可以利用父类生成实例 Son.work(d1) #并且用这个实例调用父类函数属性 d1.work() #这么调用也行
#如果子类属性与父类重名,并不会覆盖掉,而是用什么调用的就是什么
class Dad : money = 1000 def __init__(self,name): self.name=name def work(self): print('%s在工作中' %(self.name)) class Son(Dad) : money = 100000000 def __init__(self,name): self.name = name print(Son.money,Dad.money) s1 = Son('son') print(s1.name) s1.work() #子类自己生成的实例也能调用父类的函数属性
#何时使用继承呢?
1)当类之间有显著不同,并且较小的类是较大类所需要的组件时,用组合
2)当类之间有很多相同功能,提取这些共同的功能做成基类,用继承
#继承与派生
class Animals : kind = 'animal' def __init__(self,name): self.name=name def breath(self): pass def move(self): print('%s飞快的跑起来' %(self.name)) class Dog(Animals) : #继承: 得到上一级别类的类属性 def __init__(self,name): self.name=name def bark(self): #派生: 在分支类中产生的新功能,也可定义与上一级别类相同的方法名,作为新的替换 print('汪汪汪') d = Dog('花花') d.bark() d.move()
#继承同时具有两种含义
1)继承基类的方法,并且能根据自己特性做出出自己的改变或者扩展,可以解决代码重用的问题(如以上的例子)
但是尽量少用,因为这样子类和基类耦合到一块了(强耦合),写代码尽量解耦和,越独立越好
2) 归一化设计--->接口继承(常用) :
#接口其实就是一个方法(函数)
#在基类(接口类)中定义一个接口函数(不需要实现功能,pass)目的是规范子类,其子类都需要实现接口函数的功能(至少要定义这个函数),不实现就会报错(在实例化的时候)。需要利用 abc模块来完成
#接口继承实际上就是做出了一个良好抽象(如下面例子的文件),这个抽象规定了一个兼容的接口(读和写函数),使得外部调用者无需关心具体的细节(如硬盘、内存的读和写方法都是不同的,但外部的人并不需要知道,他只需要知道这是读写操作就行),这在程序设计上,叫做归一化. 例如Linux的泛文件系统
import abc class File_class(metaclass=abc.ABCMeta) : #这里需要特殊定义 '这是接口类' @abc.abstractmethod #表明以下的是一个虚拟方法,不需要具体实现 def read(self): #接口函数 pass @abc.abstractmethod def write(self): pass class Disk(File_class) : #子类 Disk 继承了接口类 #必须实现其父类(接口类)所定义的方法,不然会报错 def read(self): print('这是读取方法') def write(self): print('这是写入方法') p1 = Disk() p1.read() p1.write()
6.2 继承顺序
1)python中可以继承多个类,java和C#中只能继承一个类
2)如果继承多个类,则寻找方法的方式有两种,深度优先和广度优先,且类是经典类(深度优先),新式类(广度优先,python3中都为新式类)
#可以用以下例子测试一下
class A : def text(self): print('A') class B(A) : def text(self): print('B') class C(B) : def text(self): print('C') class D(A) : def text(self): print('D') class E(D) : def text(self): print('E') class F(C,E) : # def text(self): # print('F') pass f = F() f.text() print(F.__mro__) #查看解析顺序MRO(也就是查找的顺序) print(F.mro()) #同上是一样的
# 其实,python中在实现继承时,对于你定义的每一个类,python都会计算出一个方法解析顺序(MRO)列表,从左到右就是查找方法的顺序
而这个MRO是通过C3线性算法来实现的
MRO列表遵循以下三条准则:
1 子类会优先于父类被检查
2 多个父类会根据它们在列表中的顺序被检查
3 找到了方法就不会接着找下一个
#可以用 类名.__mro__ --> 元组的方式输出 或 类名.mro() --> 列表的方式输出 来查看这个方法解析列表(MRO)
#小补充:python2中的继承顺序
1) 新式类和python3中一样处理
2) 经典类没有__mro__方法
7 在继承中,子类中调用父类的方法(重要,实用部分)
#只利用原来的知识的调用方法如下:
class Vehicle :
def __init__(self,type,speed,load):
self.type = type
self.speed = speed
self.load = load
def accelerate(self):
print('%s开始加速,速度 %s' %(self.type,self.speed))
class Bus(Vehicle):
def __init__(self,price,type,speed,load) : #既能调用父类的功能,又能派生出自己特有的功能!!!
Vehicle.__init__(self,type,speed,load) #调用父类的__init__的函数,相当于执行了父类的__init__,即self.type=type之类的操作
#注意传入的参数
self.price = price #Bus特有的,相当于派生
def accelerate(self): #同理,既能使用父类的accelerate功能,又派生出自己的功能
Vehicle.accelerate(self) #执行了父类的accelerate方法
print('这是%s,请投入 %s 元' %(self.type,self.price)) #派生的方法
p = Bus(1,'Bus','20 m/s',100)
p.accelerate()
#以上代码,如果父类名字改变,或者变量变化,那么子类就需要改非常多,换句话说就是拓展性很差
#如果使用继承的内置方法super的话
好处1:不用在写父类的名字
好处2:不用在加self参数(具体见下面例子)
#补充:对象.__class__ 的值就是对象的那个类
class Vehicle : def __init__(self,type,speed,load): self.type = type self.speed = speed self.load = load def accelerate(self): print('%s开始加速,速度 %s' %(self.type,self.speed)) class Bus(Vehicle): def __init__(self,price,type,speed,load) : super().__init__(type,speed,load) #使用super方法,无需在写self参数 #super(Bus,self).__init__(type,speed,load) #或者这种写法,相当于super(__class__,self).__init__(type,speed,load) self.price = price def accelerate(self): super().accelerate() #同上 print('这是%s,请投入 %s 元' %(self.type,self.price)) p = Bus(1,'Bus','20 m/s',100) p.accelerate() print(p.__class__)
#小作业(选课系统)
import pickle,hashlib def create_md5(x): #以当前时间生成md5的函数,用于下面的文件命名 obj = hashlib.md5() obj.update(str(x).encode('utf8')) return obj.hexdigest() class Base : def save(self,message): with open(create_md5(message),'wb') as f : pickle.dump(self,f) #用dump的写法 # f.write(pickle.dumps(self)) #用dumps的写法 class Person(Base) : def __init__(self,name,age,School): self.name =name self.age =age self.School =School def info(self): print('学员:%s,%s岁' %(self.name,self.age)) class School(Base) : def __init__(self,location,Course,Teacher): self.location = location self.Course = Course self.Teacher =Teacher def info(self): print('XX大学%s分校' %self.location) class Course(Base) : def __init__(self,name,period,price,Class_id): self.name = name self.period = period self.price = price self.Class_id = Class_id def info(self): print('所选课程:%s,周期%s天,学费%s元' %(self.name,self.period,self.price)) class Class_id(Base) : def __init__(self,id,Teacher,time): self.id = id self.Teacher = Teacher self.time =time def info(self): print('班级号:%s,授课老师:%s,上课时间:%s' %(self.id,self.Teacher.name,self.time)) class Teacher(Base) : def __init__(self,name,major,gender): self.name = name self.gender =gender self.major =major def info(self): print('教师%s,%s,教学科目为%s' %(self.name,self.gender,self.major)) #--------------输入个人信息区域----------------- name = input('请输入姓名:') age = input('请输入年龄:') while True : print('1) 北京校区\n2) 上海校区') school = input('请选择校区:') if int(school) in [1,2]: break else: print('输入错误,请重新输入') while True : print('1) Python\n2) Linux\n3) go') course = input('请选择课程:') if int(course) in [1, 2, 3]: break else: print('输入错误,请重新输入') while True : print('1) Everyday\n2) Weekends') time = input('请选择上课时间:') if int(time) in [1, 2]: break else: print('输入错误,请重新输入') print('-------------------------------------------') #---------------实例化老师----------------------- t1 = Teacher('ssy','Python','男') t2 = Teacher('alex','Linux','男') t3 = Teacher('Elizabeth','go','女') #---------------实例化班级----------------------- if time == '1' : class_id = 1 days = '每天' else: class_id = '2' days = '周末' if course == '1' : class_teacher = t1 elif course == '2' : class_teacher = t2 elif course == '3' : class_teacher =t3 obj_class = Class_id(class_id,class_teacher,days) #---------------实例化课程----------------------- if course == '1' : obj_course = Course('Python','90',18000,obj_class) elif course == '2' : obj_course = Course('Linux','60',12000,obj_class) elif course == '3' : obj_course = Course('go','80',15000,obj_class) #---------------实例化学校----------------------- if school == '1' : obj_school = School('北京',obj_course,obj_class.Teacher) elif school == '2' : obj_school = School('上海',obj_course,obj_class.Teacher) #---------------实例化学员----------------------- P = Person(name,age,obj_school) #------------------------------------------------- #-------------------存入实例化对象------------------ P.save(name) #调用之前的存入函数 #-------------------取出实例化对象------------------ name = create_md5('ssy') P = pickle.load(open(name,'rb')) #用load的写法 # with open(name,'rb') as f : #用loads 的写法 # P = pickle.loads(f.read()) #-------------------调用对象的属性------------------ P.info() P.School.info() P.School.Course.info() P.School.Course.Class_id.info() P.School.Course.Class_id.Teacher.info() #----------------------------------------------------