面向对象
编程方式
- 面向过程
- 函数式编程
- 面向对象
面向过程
-
之前所用的,按照解决问题的步骤,一步步的按流程进行,有先后之分。整个设计流水线式的,思维比较机械化。
-
优缺点:
- 优点 : 复杂的问题流程化,将问题分解简化。
- 缺点:拓展性不足
面向对象
定义
核心是对象.对象是一个数据以及其相关行为的集合,
面对对象是功能上指向建模对象,通过数据和行为方式来描述交互对象的集合.
在python中,一切皆为对象.
优缺点
- 优点
可以解决程序的拓展性
- 缺点
就是复杂度远高于面向过程.
交互式解决问题无法准确预测结果
内容
- 类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
- **方法:**类中定义的函数。
- **类变量:**类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
- **数据成员:**类变量或者实例变量用于处理类及其实例对象的相关的数据。
- **方法重写:**如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
- **局部变量:**定义在方法中的变量,只作用于当前实例的类。
- **实例变量:**在类的声明中,属性是用变量来表示的。这种变量就称为实例变量,是在类声明的内部但是在类的其他成员方法之外声明的。
- **继承:**即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟"是一个(is-a)"关系(例图,Dog是一个Animal)。
- **实例化:**创建一个类的实例,类的具体对象。
- **对象:**通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
举例
object1:Tom
特征:
scool=zucc
name=tom
age=21
sex=male
技能:ballgame study ...
object2:Bob
scool=zucc
name=bob
age=22
sex=male
技能:tennis study ...
类就是类别,种类.
对象就是特征和技能的统一体.
类就是这一系列相似对象的特征和技能的结合.
对应现实生活,先有个体(对象),才有类别;但是对于程序,必须先有类,然后才有对象.
面向对象编程引入
定义
OOP
(object oriented programming)即面向对象编程
是一种程序设计思想.OOP
把对象作为程序的一个基本单元,一个对象就包含了数据和操作数据的函数.
在python中,所有数据类型都可以视为对象,当然也可以自定义对象.
自定义对象的数据类型就是面向对象中类的概念
举例
如要处理我们的成绩.为了表示学生的成绩:
- 面向过程的方式
st1={'name':'jack','score':80}
st2={'name':'bob','score':97}...
def show_score(st):
print(st['name'],st['score'])
show(st1)
- 面向对象
class student:
def __init__(self,name,score):
self.name=name
self.score=score
def show_score(self):
print(self.name,':',self.score)
st1=student('bob',97)
st1.show_score()
类的定义
面向对象设计的思想,先抽象出类,再根据类创建实例.
命名类名时 变量名称各个单词首字母大写
创建一个空类
class MyFirstClass:
pass
创建一个普通类
class ClassName(object):
'''dicstring(说明文档)'''
class_statement
class Student:
school='ZUCC'#所有对象的固有属性
def __init__(self,name,score):
self.name=name
self.score=score
def show_score(self):
print(self.name,':',self.score)
st1=Student('bob',97)#实例化
print((st1.name,st1.score,st1.school))
('bob', 97, 'ZUCC')
总结
类的作用是一个模板.我们可以在创建实例的时候,把一些我们认为必须要绑定的属性填写进去.这时就通过特殊的__init__
方法.在创建实例的时候,绑定相关的属性.
和普通函数相比,在类中定义方法时,第一个参数必须是self.除第一个参数外,其他的与普通函数没有什么区别.
self代表的是实例,而非类
__init__
方法
- 为对象初始化自己独有的特征
- 该方法中可以有任意的代码,但是不能有返回值
数据封装
class Student:
school='ZUCC'#所有对象的固有属性
def __init__(self,name,score):
self.name=name
self.score=score
st1=Student('bob',97)#实例化
我们通过__init__
让st1
实例本身有了相关的数据,我们可以直接在Student类的内部定义相关的函数来访问数据,以此封装数据.
这些封装数据的函数和Student类本身是关联起来的,他们被称之为方法.
类的两个作用
-
类名引用
类名.属性
-
实例化
类名加上一个括号就是实例化,它能够自动触发__init__
函数,进而为每个实例定制自己的特征
- 类属性的补充
类属性的查看
-
dir
()来查看对应类包含的属性(返回列表) -
类名.
__dict__
返回一个字典,key为属性名,value为属性值
特殊的类属性
类名.__name__
返回类的名字
类名.__doc__
返回类的文档字符串
类名.__base__
返回类的第一个父类
类名.__bases__
返回类的所有父类(元组)
类名.__module__
返回类定义所在的模块
类名.__class__
返回实例对应的类
对象之间的交互
定义2个类,Person,Dog,都有体力及攻击值,可以进行攻击
class Person:
def __init__(self,name,aggressivity,life_value):
self.name=name
self.aggre=aggressivity
self.life_values=life_value
def attack(self,dog):
dog.life_values-=self.aggre
class Dog:
def __init__(self,name,breed,aggressivity,life_vale):
self.name=name
self.breed=breed
self.aggre=aggressivity
self.life_values=life_vale
def bite(self,people):
people.life_values-=self.aggre
per=Person('bob',10,888)
dog=Dog('white','husky',12,480)
print('dog:',dog.life_values,',person:',per.life_values,sep='')
per.attack(dog)
dog.bite(per)
print('dog:',dog.life_values,',person:',per.life_values,sep='')
dog:480,person:888
dog:470,person:876
类命名空间与对象实例的空间
创建一个类就会创建一个类的名称空间,用来存储我们定义的所有的变量名。这些名字就是属性。
类的属性有两种
-
静态 :直接在类中定义的变量
-
动态 : 在类中定义的方法
静态属性是共享给所有对象的,动态属性是绑定到所有对象的
class Student:
school='ZUCC'#所有对象的固有属性(静态)
def __init__(self,name,score):
self.name=name
self.score=score
def find_score(self): ##(动态)
print(self.name,":",self.score)
st1=Student('bob',95)
st2=Student('jack',89)
print(id(st1.school),id(st2.school))
print(id(st1.find_score),id(st2.find_score),id(Student.find_score
4365888 4365888
4014768 4014768 12138368
类的三大特性
继承
在面向对象编程中,当我们定义一个新的类后,可以从某个现有的类,继承,新的类就被称为子类
SubClass
,而被继承的类则被称为基类,父类,超累(Base Class,Father Class,Super Class
)
#定义一个动物类(Animal),它有一个run()方法
class Animal: #定义父类
def run(self):
print('Animal is running.')
class Dog(Animal): #括号内为所继承的类
pass
class Cat(Animal): #单继承
pass
class Husky(Animal,Dog): #多继承用逗号分隔开
pass
Dog().run() #继承Animal的run
Cat().run()
print(Dog.__bases__) #查看该类继承的父类
print(Animal.__bases__)
Animal is running.
Animal is running.
(<class '__main__.Animal'>,)
(<class 'object'>,)
如果不指定类,python类会默认继承object类
object是所有python类的基类,提供一些常见方法的实现
多态
当子类和父类存在相同名称的方法时,子类的方法会覆盖父类的方法,在运行代码时,总会调用子类的方法。
这样就是继承的另外一个好处,多态。
理解多态,首先要对数据类型再进行说明。定义一个类的时候实际上就是定义了一种数据类型。我们自定义的数据类型和python自带的数据类型(str,list,dict)等相同。
class Animal:
def run(self):
print('Animal is running.')
class Dog(Animal):
def run(self):
print('Dog is Running')
class Cat(Animal):
def run(self):
print('Cat is Running')
dog=Dog()
cat=Cat()
dog.run()
cat.run()
Dog is Running
Cat is Running
###isinstance判断所给的对象是不是所给的数据类型
li=[]
print(isinstance(li,list))
print(isinstance(dog,Animal))
print(isinstance(Dog,object))
True
True
True
对于一个变量,只要知道其父类型,无需确切知道其子类型,就可以调用相关的方法。运行时具体的方法是作用于子类型上还是作用于父类型上,由我们运行的对象决定。也就是说,在调用时只管调用,无需注意细节
当我们新增一个子类时,只要保证相关的方法编写正确,就不用再管原来的代码是怎么调用的
开闭原则
- 对拓展开放:允许新增子类
- 对修改封闭:不需要修改依赖父类类型的函数
总结
继承可以一级一级的继承下来
任何类都可以追溯到根类object
私有属性
在类的内部,可以有属性和方法,而外部代码就可以通过直接调用实例变量的方法来操作数据.这样,隐藏了内部的复杂逻辑
class Student:
school='ZUCC'#所有对象的固有属性(静态)
def __init__(self,name,score):
self.name=name
self.score=score
def find_score(self): ##(动态)
print(self.name,":",self.score)
st1=Student('bob',95)
st1.find_score()
st1.score=92
st1.find_score()
bob : 95
bob : 92
由上可知,外部代码可以自由修改一个实例的属性(name,score).
如果要让内部属性不被外部访问,我们可以在属性名称前面加__(2个下划线.)
在python中如果实例的变量名以双下划线开头,就变成了一个私有变量,只允许内部访问.
class Student:
school='ZUCC'#所有对象的固有属性(静态)
def __init__(self,name,score):
self.name=name
self.__score=score
def find_score(self): ##(动态)
print(self.name,":",self.__score)
st1=Student('bob',95)
st1.find_score()
print(st1.name)
print(st1.__score)
bob : 95 #成绩只能通过内部函数访问
bob #可以直接外部访问
AttributeError: 'Student' object has no attribute '__score' #报错,无法直接无法及更改分数
如果想要更改私有变量,可以通过在类内定义更改该变量的函数,再在外部调用函数
class Student:
school='ZUCC'#所有对象的固有属性(静态)
def __init__(self,name,score):
self.name=name
self.__score=score
def find_score(self): ##(动态)
print(self.name,":",self.__score)
def change_score(self,score):
self.__score=score
st1=Student('bob',17)
st1.find_score()
st1.change_score(89)
st1.find_score()
bob : 17
bob : 89
########注:并非绝对无法从外部访问私有变量
print(st1._Student__score)
89
#可以以以上方式访问私有属性,但是不推荐这种方式.
封装
定义
隐藏对象的属性和实现细节,仅对外提供公共访问的方式
优点
- 可以将变化隔离
- 便于使用
- 提高安全性
- 提高复用性
原则
-
将不需要对外提供的内容隐藏
-
隐藏属性,提供公共方法对其进行访问
————>私有方法,私有变量------->私有属性
用双下划线开头标记私有属性
鸭子类型
解释
关于鸭子类型,在百科中是这么描述的:“鸭子类型”的语言是这么推断的:一只鸟走起来像鸭子、游起泳来像鸭子、叫起来也像鸭子,那它就可以被当做鸭子。也就是说,它不关注对象的类型,而是关注对象具有的行为(方法)。对于C++来说,我们要调用某个以类A为参数的函数,则我们传入的类型必须是类A类型,或者是类A的子类类型。但在Python中,却没有这个要求,它只要求我们在函数体中实现部分,在类B中同样也能实现,那么B就能传入函数中。
class obj1:
def run(self):
print('this is first')
class obj2:
def run(self):
print('this is second')
def runs(obj1):
obj1.run()
StuA=obj1()
runs(StuA)
infA=obj2()
runs(infA)
this is first
this is second