一 对象的概念
# “面向对象”的核心是“对象”二字,而对象的精髓在于“整合“,什么意思?
所有的程序都是由”数据”与“功能“组成
因而编写程序的本质就是定义出一系列的数据
然后定义出一系列的功能来对数据进行操作。
"对象"是把数据与功能整合到一起的产物,或者说”对象“就是一个盛放数据与功能的容器/箱子/盒子。
面向过程:
核心是"过程"二字
过程的终极奥义就是将程序流程化
过程是"流水线",用来分步骤解决问题的
优点是:复杂度的问题流程化,进而简单化
缺点是:一套流水线或者流程就是用来解决一个问题,改动很麻烦
应用场景:
一旦完成基本很少改变的场景,著名的例子有Linux內核,git,以及Apache HTTP Server等。
面向对象:
核心是"对象"二字
对象的终极奥义就是将程序"整合"
对象是"容器",用来盛放数据与功能的
类也是"容器",该容器用来存放同类对象共有的数据与功能
缺点:编程的复杂度高
优点:扩展性强,可维护性强
应用场景:
需求经常变化的软件,一般需求的变化都集中在用户层,
互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方。如:微信、qq
# 需要强调的是,面向对象解决的仅仅只是扩展性问题
软件质量属性包含很多方面:安全性、可拓展性、可维护性、可移植性、可伸缩性、可靠性、成本、性能
二 类与对象
# 类即类别/种类
是面向对象分析和设计的基石
如果多个对象有相似的数据与功能,那么该多个对象就属于同一种类
# 对象是"容器",用来盛放数据与功能的
# 类也是"容器",该容器用来存放同类对象共有的数据与功能
# 强调:
1.在程序中,必须要事先定义类,然后再调用类产生对象(调用类拿到的返回值就是对象)
2.产生对象的类与对象之间存在关联
对象可以访问到类中共有的数据与功能
所以类中的内容仍然是属于对象的,类只不过是一种节省空间、减少代码冗余的机制
面向对象编程最终的核心仍然是去使用对象
三 面向对象编程
3.1 类的定义与实例化
# 一:类的定义
类是对象相似数据与功能的集合体
所以类体中最常见的是变量与函数的定义,但是类体其实是可以包含任意其他代码的
# 注意:
1.类体代码是在类定义阶段就会立即执行,会产生类的名称空间 (局部名称空间)
定义完类,可在类.__dict__查看名称空间
class Student:
# 1.共有数据属性的定义
stu_school='oldboy'
# 2.对象独有数据的定义 (通过类实例化时自动调用的初始化方法)
该方法会在对象产生之后自动执行,专门为对象进行初始化操作
可以有任意代码,但一定不能返回非None的值
def __init__(self,name,sex,age):
self.name=name
self.sex=sex
self.age=age
# 3.共有功能属性的定义
def tell_stu_info(self):
print('学生信息:名字:%s 年龄:%s 性别:%s' %(
self.stu_name,
self.stu_age,
self.stu_gender
))
def set_info(self,x,y,z):
self.stu_name=x
self.stu_age=y
self.stu_gender=z
def choose(self,x):
print('正在选课')
self.course=x
# 二:类的实例化 调用类产生对象
调用类的过程又称之为实例化,发生了三件事:
1.先产生一个空对象 通过类.__new__()
2.python会自动调用类中的__init__方法,
然后将空对象以及调用类时括号内传入的参数,一同传给__init__方法
3.返回初始完的对象
stu1_obj=Student('egon',18,'male') # 本质:Student.__init__(空对象,'egon',18,'male')
stu2_obj=Student('lili',19,'female')
stu3_obj=Student('jack',20,'male')
# 总结__init__方法:
1.会在调用类时自动触发执行,用来为对象初始化自己独有的数据
2.__init__内应该存放是为对象初始化属性的功能
但是可以存放任意其他代码,想要在类调用时就立刻执行的代码都可以放到该方法内
3.__init__方法必须返回None
# 注意:
1.类的名字使用驼峰体,变量命名使用下划线
2.类与函数的区别:
1.类的名称空间在类定义时产生;函数的名称空间是在函数调用阶段才会产生
2.调用类的过程称为将类实例化,拿到的返回值就是程序中的对象,或称为一个实例。
但并不会执行类中的代码体,因为类中代码体在定义阶段已经执行了
而函数中的代码体在调用阶段执行。(函数在定义时,只是声明了函数名的名称空间)
3.2 属性访问
3.2.1 类属性与对象属性
# 类中存放的是对象共有的数据与功能
# 一.类属性是共享的
# 1.类可以访问:
1.类的数据属性
print(Student.stu_school) # 等同于Student.__dict__['stu_school']
2.类的函数属性
print(Student.tell_stu_info)
print(Student.set_info)
# 除了查看属性
Student.attrib=value # 修改或新增属性
del Student.attrib # 删除属性
# 2.对象也可以访问:
stu1_obj.name # 查看 等同于stu1_obj.__dict__[‘name']
stu1_obj.course='python' # 新增 等同于stu1_obj.__dict__['course']='python'
stu1_obj.age=38 # 修改 等同于stu1_obj.__dict__['age']=38
del stu1_obj.course # 删除 等同于del stu1_obj.__dict__['course']
# 二.类属性与对象属性
# 1.类中定义的变量是类的数据属性,是共享给所有对象用的,大家访问的地址都一样
print(id(Student.stu_school)) # 4301108704
print(id(stu1_obj.stu_school)) # 4301108704
print(id(stu2_obj.stu_school)) # 4301108704
# 若修改类的数据属性,则所有对象包括类的数据属性都会修改
Student.stu_school='OLDBOY'
print(Student.stu_school) # 'OLDBOY'
print(stu1_obj.stu_school) # 'OLDBOY'
print(stu2_obj.stu_school) # 'OLDBOY'
# 若修改对象的数据属性,则只有对象本身数据属性会修改
原理:赋值操作,会从自身对象中,查找是否有该属性;若是有直接修改,若是没有直接新建;
新建后,自身该数据属性已存在,就不会访问类共有的该数据属性(或者说是覆盖了类的共有该数据)
stu1_obj.stu_school='OLDBOY'
print(stu1_obj.stu_school) # 'OLDBOY'
print(Student.stu_school) # 'oldboy'
print(stu2_obj.stu_school) # 'oldboy'
# 2.类中定义的函数是类的函数属性
主要是给对象使用的,而且是绑定给对象的,虽然所有对象指向的都是相同的功能
但是绑定到不同的对象就是不同的绑定方法,内存地址各不相同
# 类可以使用,但必须遵循函数的用法,有几个参数需要传几个参数
Student.tell_stu_info(stu1_obj) # 第一个参数:对象 就不能省略!
Student.set_info(stu1_obj,'EGON',19,'MALE')
# 绑定方法的特殊之处在于:绑定给谁就应该由谁来调用,就会将’谁’本身当做第一个参数自动传入
stu1_obj.tell_stu_info() # tell_stu_info(stu1_obj)
stu2_obj.tell_stu_info() # tell_stu_info(stu2_obj)
stu1_obj.choose('python全栈开发')
stu2_obj.choose('linux运维')
# 注意:
绑定到对象方法的这种自动传值的特征,决定了在类中定义的函数都要默认写一个参数self
self可以是任意名字,但命名为self是约定俗成的。
3.2.2 属性查找顺序
# 属性的查找顺序:
对象的名称空间里只存放着对象独有的属性
而对象们相似的属性是存放于类中的
对象在访问属性时,会优先从对象本身的__dict__中查找
未找到,则去类的__dict__中查找
3.3 绑定方法
# 一、绑定方法
绑定方法:特殊之处在于将调用者本身当做第一个参数自动传入
# 1.绑定给对象的方法:调用者是对象,自动传入的是对象 (类也可以调用)
在类中书写方法,默认绑定给对象使用的 参数名称:self
# 2.绑定给类的方法:调用者类,自动传入的是类
classmethod :将下面的函数装饰成,绑定给类的方法 参数名称:cls
class Mysql:
def __init__(self,ip,port):
self.ip=ip
self.port=port
def func(self): # 类中不加任何装饰器的函数,都是绑定给对象的方法
print('%s:%s' %(self.ip,self.port))
@classmethod # 将下面的函数装饰成,绑定给类的方法
def from_conf(cls):
print(cls)
return cls(settings.IP, settings.PORT)
obj2=Mysql.from_conf()
print(obj2.__dict__) # {'ip':'127.0.0.1', 'port':3306}
# 二、非绑定方法 (静态方法)
没有绑定给任何人:调用者可以是类、对象,没有自动传参的效果,就是普通函数
class Mysql:
def __init__(self,ip,port):
self.nid=self.create_id()
self.ip=ip
self.port=port
@staticmethod # 将下述函数装饰成一个静态方法
def create_id():
import uuid
return uuid.uuid4()
obj=Mysql('1.1.1.1', 3306)
print(Mysql.create_id) # 类可以调用 <function Mysql.create_id at 0x0000018367B8AAE8>
print(obj1.create_id) # 对象也可以调用 <function Mysql.create_id at 0x0000018367B8AAE8>
print(Mysql.create_id)
print(Mysql.f1) # <bound method Mysql.f1 of <class '__main__.Mysql'>> 类方法
print(obj1.f2) # <bound method Mysql.f2 of <__main__.Mysql object at 0x0000028DE0E59A90>> 对象方法
3.4 总结
# Python中一切皆为对象,且Python3中类与类型是一个概念!
在上述介绍类与对象的使用过程中,我们更多的是站在底层原理的角度去介绍类与对象之间的关联关系
如果只是站在使用的角度,我们无需考虑语法"对象.属性"中 "属性"到底源自于哪里
只需要知道是通过对象获取到的就可以了。
对象是一个高度整合的产物,有了对象
只需要使用”对象.xxx“的语法就可以得到跟这个对象相关的所有数据与功能
十分方便且解耦合程度极高