一.面向对象
1.在进入面向对象编程之前,我们要先了解什么是面向过程编程。
过程指的是解决问题的步骤,面向过程的目的是为了将复杂的问题简单化,换句话说就是将一个复杂的大问题拆分成一个个简单的小问题,按照步骤一一去解决。但是这种编程思想有个致命的缺点,就是由于其流程是固定的,如果中间的某个环节发生修改,那么导致整体都要进行修改,即扩展性差,维护性低。这个缺点也就导致了这种编程思想只适用于对扩展性要求比较低的程序开发中(如:系统内核,脚本程序,Apache HTTP服务器)。
2.介于面向过程(oop(ogject oriented Programming))编程思想的缺陷,就催生了新的编程思想,面向对象的编程思想就诞生了。它是将程序看作是一堆对象的集合体,其实现功能的方式是通过对象之间的交互来完成的。面向对象编程思想核心概念就是类与对象,其本质是使用不同的对象来完成程序。
什么是对象?一切皆对象,指的是具备某些特征和技能的结合体,是一种实实在在的具体物体。我们通过对对象进行描述就可以找到对象。
什么是类?类是一种抽象的概念,它指的是一些具备相同特征和技能的对象集合。
类与对象的关系:类包含了一系列相同特征和技能的对象,对象是属于某个类的实例。
在程序中:先有类,再有对象,通过类产生对象,要先确定对象具备的特征和技能,才能产生对象。
面向对象的优缺点:
优点:不用考虑过程(步骤),扩展性好,耦合度低,维护性高。
缺点:复杂度比面向过程高,而且无法预知执行结果。
应用场景:对扩展性要求比较高时(如:与用户交互的程序(QQ,微信等))。
二.类和对象
1.类和对象的创建
通过关键字class进行创建,class后面跟类名,类的名字按照大驼峰的方式来书写,即:单词首字母大写。在类中描述对象的特性和行为,用变量来描述特征。得到对象,我们需要通过调用类(调用方式:类名加括号),来创建对象。
# 在类中描述对象的特征和行为 class Person: # 用变量来描述特征 name = "李四" sex = "man" age = 20 # 得到对象 通过调用类 ,也称之为实例化 或 创建对象 obj = Person() print(obj) #<__main__.Person object at 0x00000279452B7C50> # 模块名为main 其包含一个Person类 通过Person类产生了一个对象 地址为0x00000279452B7C50 # 这是一个Person类的对象 其地址为0x00000279452B7C50 # 使用对象的属性(说的就是特征) print(obj.name) print(obj.age) print(obj.sex)
对于对象来说,每个对象的内存地址都不同,在创建对象时,会申请新的内存空间,将对象中的内容存进去。
存储属性的位置有两个:类、对象。当每个对象的某个特征都相同时,则放入类中;当每个对象的特征都不相同时,则放入对象中。我们可以通过__dict__方法获取一个对象或者类中包含的所有内容,如果要修改对象的属性,可以通过点语法来实现,具体方法如下:
class Student: number = "007" name = "盖伦" sex = "man" age = 18 # 学生的学校 由于每个学生的学校都是相同 所以将其放到类中 school = "Tsinghua" # 创建了两个学生对象 stu1 = Student() stu2 = Student() print(stu1) print(stu2) # 每个对象内存地址都是不同的 , 在创建对象时,计算机会申请一个新的内存空间,并将对象中的内容存进去 print(id(stu1.name)) print(id(stu2.name)) # 由于name的值时声明在类中的,所以每个对象使用的都是同一份 # 为对象单独制定属性 stu1.name = "韩信" stu2.name = "陈大炮" print(stu1.name) print(stu2.name) # 每个对象的name属性都不同,则意味需要给每个对象单独指定name # 存储属性的位置有两个 一个是类中,还有一个对象中 # 当每个对象的某个特征都相同时则放到类中 # 当每个对象的某个特征都不同时则放到对象中 # 通过__dict__可以获取一个对象中包含的内容 stu1.age = 30 print(stu1.__dict__) print(stu2.__dict__) # 获取类中包含的内容 print(Student.__dict__)
属性的访问顺序:当对象中不存在该属性时,会去类中寻找;如果存在时,则直接访问对象中的属性。
"属性的访问顺序" class Car: c_type = "破鞋" color = "red" price = 400000 c1 = Car() c2 = Car() print(c1.__dict__) print(c2.__dict__) print(c1.c_type) # 当对象中不存在是会到类中去找 c1.c_type = "法拉利" print(c1.__dict__) print(c2.__dict__) print(c1.c_type) print(c2.c_type) # 如果对象中存在这个属性,优先访问对象中的属性 print(Car.__dict__) # 查找顺序为 对象 -> 类 # 当创建一个类的时候 会产生名称空间,存储类中名称和值的绑定关系 # 当创建一个对象的时候 会产生名称空间,存储对象中名称和值的绑定关系 # 类还有另一个作用 就是 作为对象的模板,所有属于同一个类的对象,都具备类中的公共内容 # 即使我们什么都不写 类中也存在一些自带的属性,是从父类得到的(继承会详细讨论) class A: pass print(A.__dict__)
三.初始化函数
初始化函数时定义在类中的函数,其目的是为对象设置属性。但是如果没有对象,那么初始化函数没有任何的意义,因此初始化函数与类是一个整体。还有一点就是对象的创建与初始化步骤是分开的,现实情况下是对象一经创建,就应该进行初始化,所以对象的创建与初始化应该绑定在一起。
初始化函数名称是固定的(__init__),该函数会在调用类时自动执行,而且函数会有一个默认参数self,这个默认参数是对象本身,调用时会自动传入对象。
# class Student: # # 定义一个函数 用于为对象设置属性 # def set_attr(obj, name, sex, age): # obj.name = name # obj.sex = sex # obj.age = age # pass # # # stu1 = Student() # # 指定属性 # stu1.name = "张无忌" # stu1.sex = "man" # stu1.age = 18 # # set_attr这个函数目的是用于设置对象的属性 ,如果没有对象则该函数没有存在的意义,也就是初始化函数与类应该是一个整体 # # 应该讲这个函数放到类中 # stu3 = Student() # stu4 = Student() # # # Student.set_attr(stu3,"金毛狮王","man",80) # print(stu3.__dict__) # # # Student.set_attr(stu4,"张全蛋","man",28) # print(stu4.__dict__) # # # # 现在已经简化了代码 但是对象的创建和初始化步骤是分开的, 通常对象一旦创建 就应该进行初始化,所以最好时将创建与初始化进绑定 # # stu5 = Student() # print(stu5) # # 作为一个人 一旦出生 性别必须要指定 # 带有__开头__结尾的函数 是一些特殊内置函数,会在某个时间点自动触发执行 class Person: # 初始化函数名称是固定 该函数会在调用类是时自动执行,self参数必须有,表示要进行初始化的对象,系统会自动传值 def __init__(self,name,age): print("执行了 __init__") print(self) self.name = name self.age =age p1 = Person("张三丰",80) print(p1.__dict__) p2 = Person("李狗蛋",20) print(p2.__dict__) # init 函数用于初始化对象,它会在创建对象时,自动执行,并传入调用类时传递的参数,第一个参数表示要初始化的对象本身, # self(第一个)参数不需要手动传递 # self表示对象自己 是一个形式参数,名字可以随便取,但是不建议修改
四.绑定方法与非绑定方法
绑定:指的是将两个东西绑定在一起,方法:指的是函数。
绑定方法就是把对象与函数进行绑定。调用函数就是调用对象的方法,对象本质上是一种存放数据的容器,函数是一堆处理数据的代码,因此绑定方法就是将数据与处理数据的函数进行绑定。
为什么要把数据与处理数据的函数绑定在一起?
1.传递参数,必须手动传递,很有可能传参顺序而发生错误
2.每次处理数据 都需要手动传参数
3.当要处理的数据特别的多 就不能再定义为变量了 你可以使用列表出来存储要处理的数据但是 每次处理 都需要先获取数据 在传递给处理数据的函数
综上所述:要把处理的数据与处理数据的函数进行绑定,为了简化代码,提升效率。
如何用绑定方法?
绑定方法分为两种:1.绑定给对象,2.绑定给类
在默认情况下,在类中定义的函数都是绑定方法,而且都会把对象作为函数的第一个参数self。当用对象来调用类中的方法时,默认把对象传入方法中,这是绑定给对象的方法。我们也可以用类名类调用,但是需要手动传入对象。
绑定给类的方法:使用一个装饰器(classmethod)装饰该函数,这个函数也有一个默认参数cls。这种方法对象和类都可以调用,并且都不需要手动传参。
总结:当要处理的数据包含在类中时,应该绑定给类;当要处理的数据包含在对象中时,应该绑定给对象。
class Student: school = "beijing" def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex # 绑定方法分为两种 一种是绑定给对象的,一种绑定给类的 # @classmethod # def print_school_name(): # print("学校名为:%s" % Student.school) # 绑定给类的方法 使用一个装饰器叫classmethod,必须有一个参数,表示当前类,参数名也可以自己定义,建议不要修改 @classmethod def print_school(cls): # 输出类里面叫school的属性 print(cls.school) # def print_school2(self): # 输出类里面叫school的属性 # print(self.school) # 这是绑定给对象的方法 def sayHello(self): print(self.name, " 说: 你好") # # Student.print_school_name() # # Student.print_school() # 对象绑定方法 可以使用对象来调用 也可以使用类名来调用 # 在对象调用时会自动传入对象自己 # 类调用时不会自动传参 # 类的绑定方法,对象和类都能调用,并且都会自动传入这个类 # Student.print_school() # stu1 = Student("印度阿三","woman",20) # stu1.print_school() # 如何定义对象绑定方法 以及类的绑定方法 # 在调用时的区别 # 一个方法到底应该绑定给对象还是帮对给类? # 当要处理的数据包含在类中时,就应该绑定给类 # 当要处理的数据包含在对象中时,就应该绑定给对象 stu10 = Student("赵六","男",20) # Student.print_school2(stu10) # 有一个Dog类 每一个Dog对象都应该会叫 会跑 请用面向对象来完成 class Dog: def __init__(self,nikename,gender,age): self.nikename = nikename self.gender = gender self.age = age def run(self): print("不好了 ",self.nikename,"跑了 ") def bark(self): print("听",self.nikename,"在瞎叫...") d1 = Dog("大金毛","母的",2) d2 = Dog("大黄","公的",3) d1.run() d2.bark() """ # 类的绑定方法和对象的绑定方法的相同与不同 相同点: 1.都会自动传值 2.都可以被类和对象调用 不同点: 1.对象绑定方法再对象调用时 传的是对象自己 而类绑定方法字自动传的是类自己 2.第一个参数 个cls 一个叫self 为什么要绑定? # 第一个问题传递参数,必须手动传递,很有可能传参顺序而发生错误 # 第二个问题每次处理数据 都需要手动传参数 # 第三个问题 当要处理的数据特别的多 就不能再定义为变量了 你可以使用列表出来存储要处理的数据 但是 每次处理 都需要先获取数据 在传递给处理数据的函数 之所以绑定 ,简化代码,提高效率 """
什么是非绑定方法?在类中,既不绑定给对象,也不绑定给类的方法。使用一个装饰器(staticmethod)装饰该函数。
特点:没有自动传参数的效果 ,类和对象都能调用,就是一个普通函数。当你的这个功能不需要访问类的数据 也不需要访问对象的数据,就可以作为一个非绑定方法,使用场景较少。
class Teacher: def __init__(self,name,sex): self.name = name self.sex = sex # @staticmethod 用于定义个非绑定方法 @staticmethod def test_func(num): print("test_func run!") print(num) Teacher.test_func(1) t1 = Teacher("矮根","男") t1.test_func(100) print(t1.test_func)