20181213(面向对象编程,初步)
一、面向过程与面向对象的编程
1、面向过程的编程:将复杂问题拆分成若干小问题,按照步骤流程一一解决。也可以说是将复杂问题流程化,为其制定一个固定的实现流程。
优点:复杂问题简单化
缺点:可拓展性差,维护性差。一旦某一步骤出现问题,后续步骤可能无法进行。
应用场景:对扩展性要求较低的软件,如系统内核、脚本程序。
2、面向对象的编程:OOP(Object Oriented Programming)。将程序看作是一堆对象的集合,通过对象之间交互来实现功能。面向对象编程的本质就是使用不同的对象来完成程序。
优点:不用考虑实现步骤,从具体的执行者变成指挥者。扩展性高,对象与整体流程耦合度低;对象与对象之间的耦合度也低。可维护性高。
缺点:相比面向过程,更为复杂,无法预知执行结果。
应用场景:需要较高的扩展性(直接面向消费级用户的程序)。 对于不需要扩展性的程序,使用面向对象的编程思维反而会使问题复杂。
二、类与对象
面向对象思想中最核心的概念就是 类与对象:
对象:一切皆对象,对象指的是具备某些特征与技能的结合体,是具体的物体(如我的手机)。对象本质上就是一种存放数据的容器。
类:类就是类型、类别、分类。类是一个抽象概念,是一些具备相同特征和技能对象的集合体。
类与对象的关系:
类的作用是表示对象与对象之间的相同点,通过类就能大致了解一个对象的特征。
对象是属于某个类的实例。
在现实世界,先有对象,后有类。
在程序中,先有类,后有对象。
类名要按照大驼峰的方式来书写,例如 ThisIsPerson 每个单词首字母都大写。在类中描述对象的特征和行为。
class Person: # 此处没有括号
# 用变量来描述共同特征
name = "alex"
sex = "man"
age = 22
# 得到对象,通过调用类得到对象,也称之为实例化 或 创建对象
obj = Person()
print(obj)
输出结果为:
<__main__.Person object at 0x00000202FF209400> #Person类下的一个对象,内存地址是0x00000202FF209400
# 使用对象的属性(也就是特征)
print(obj.name)
print(obj.sex)
print(obj.age)
输出结果为:
alex
man
22
类中的增删查改:
class Person:
name = "alex"
print(Person.name) # 查
Person.name = "egon" # 改
print(Person.name)
Person.age = 22 # 增
print(Person.age)
del Person.name #删
只要对类的属性进行了修改,就会立刻反映给所有对象,而对象却无需修改。
每个对象的内存地址都是不同的,在创建对象时,计算机会申请一个新的内存空间,并将对象中的内容存进去
存储属性的位置有两个,分别是类中和对象中. 当每个对象的某个特征都相同时则放到类中. 当每个对象的某个特征都不同时则放到对象中.
#通过__dict__可以获取一个对象中包含的内容
print(obj.__dict__)
# 获取类中包含的内容
print(Student.__dict__)
当对象中不存在时,会到类中去寻找,类中没有,就去父类中查找。
如果对象中存在这个属性,优先访问对象中的属性
属性查找顺序:对象---->类
当创建一个类的时候,会产生名称空间,存储类中的名称和值的绑定关系. 当创建一个对象的时候,会产生名称空间,存储对象中的名称和值的绑定关系.
类还有一个作用就是作为对象的模板, 所有属于同一个类的对象,都具备类的共同属性.
即使我们什么都不写,类中也会存在一些自带的属性,是从父类中继承的.
三、初始化函数
初始化函数应该与类是一个整体,应该将函数放入类中。 通常对象一旦创建就应该进行初始化,创建与初始化进行绑定。
初始化要用到init方法,该方法是对象产生之后才会执行,只用来为对象进行初始化操作。
class Person:
# 初始化函数名称是固定的,该函数会在调用类时自动执行
# self形参必须有,但会自动获取,无需实参。self名称可自定义,但不建议修改,毕竟约定俗成。
def __init__(self,name,age):
print("执行了__init__")
print(self) # self就是要进行初始化的对象,系统会自动传值
self.name = name
self.age = age
p1 = Person("alex",33) #从类Person中获取一个名叫p1的对象,对应的参数是"alex",33
print(p1.__dict__) #结果是一个字典
输出结果:
执行了__init__
<__main__.Person object at 0x000001DD789E9400> #类Person下一个对象的内存地址
{'name': 'alex', 'age': 33}
__init__方法
强调:
1、该方法内可以有任意的python代码
2、一定不能有返回值
四、绑定方法
方法的定义:为了方便理解把函数称之为方法。
绑定方法:绑定方法是把对象与函数进行绑定。调用函数就变成了调用函数的方法。绑定方法就是将数据与处理数据的函数绑定在一起。
绑定方法分为两种,一种是绑定给对象的,一种是绑定给类的。
默认是绑定给对象的。
class Student: #建立一个Student的类
school = "BeiJing" #类的共同属性是学校名叫BeiJing
def __init__(self,name,sex,age): # 初始化函数,注意参数与定义的方法。
self.name = name
self.sex = sex
self.age = age
def sayHi(self): #与函数的初始化同级
print("hello my name is %s my age %s my sex %s" %(self.name,self.age,self.sex))
# 默认情况下,在类中定义的函数都是绑定方法,共同点是都会将对象作为第一个参数self。即对象必定要有,并以参数身份传入函数中。
stu1 = Student("egon","male",22)
# 当用对象来调用类中的方法时,默认把对象传入方法中
# 用类名来调用时,则需要手动传入对象
stu1.sayHi()
Student.sayHi(stu1)
输出结果:
hello my name is egon my age 22 my sex male
hello my name is egon my age 22 my sex male
print(stu1.sayHi)
# 这是一个绑定方法,本质上是Student类中的sayHi函数绑定给了地址为。。。的对象
# 只要拿到对象,就同时拿到了数据和处理数据的方法
输出结果:
<bound method Student.sayHi of <__main__.Student object at 0x000002471E089B38>>
绑定给类的方法:
使用一个装饰器classmethod,必须有一个参数cls用来表示当前类,参数名可以自定义,但不建议修改(约定俗成,没有理由)。
class Student:
school = "BeiJing"
def __init__(self,name,sex,age):
self.name = name
self.sex = sex
self.age = age
当要处理的数据包含在类中时,就应该绑定给类。 当要处理的数据包含在对象中时,就应该绑定给对象。
总结:
对象绑定方法,可以使用对象来调用,也可以使用类名来调用
在对象调用时会自动传入对象自己,类调用时不会自动传参
类的绑定方法,对象和类都能调用,并且都会自动传入这个类
类的绑定方法和对象的绑定方法的异同点
相同点:
1.都会自动传值
2.都可以被类和对象调用
不同点:
1.对象绑定方法在对象调用时,传的是对象自己,而类绑定方法自动传的是类自己.
2.第一个参数 一个cls 一个是self
五、非绑定方法
非绑定方法:即在类中的函数,既不绑定给类,也不绑定给对象。 特点:没有自动传参的效果,类和对象都能调用,就是一个普通函数。 应用场景:当你的这个功能既不需要访问类的数据,也不需要访问对象的数据,就可以作为一个非绑定方法。
class Teacher:
def __init__(self,name,sex):
self.name = name
self.sex = sex
# @staticmethod 用于定义一个非绑定方法
六、数据存取练习
import json
class Student:
school = "beijing"
def __init__(self,name,sex,age,classes): #初始化
self.name = name
self.age = age
self.sex = sex
self.classes = classes
def save(self): # 默认绑定给对象的函数
dic = {"name":self.name,"sex":self.sex,
"age":self.age,"classes":self.classes} # 需要自己去创建字典
with open(self.name,"wt",encoding="utf-8") as f: # 文件名就是学生名
json.dump(dic,f) # 序列化数据