面向对象基础语法
面向对象基础
面向过程编程
在讲面向对象前,我们已经学过了用函数进行编程,进而实现某些功能,那这个实现功能的过程就是面向过程编程
面向过程:
面向过程的 核心就是 “过程 “二字,面向过程的特性总的来讲就是将要实现的需求分解成若干个小功能,进而一步一步的去实现整个需求
但是也就是这个特性,当你需要对 某个功能进行修改或者扩展的时候,就会牵一发而动全身,你不单单要修改这个功能所在的代码块,整体的的代码你也需要进行修改。
适用场景:
面向过程编程适用于扩展性较低的程序,比如某个shell脚本;但是对于大型的程序而言面向过程编程的缺点就显而易见
面向过程的缺点:可扩展性差
那么当我们的需求过大时,我们应该怎样取写呢?此时就是面向对象编程大显身手的时候了!
面向对象编程(OOP)
OOP编程是利用“类”和“对象”来创建各种模型进而来实现需求,
面向对象的优点:
- 程序的维护和扩展变得更简单,并且可以大大提高程序开发效率
- 基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。
面向对象的缺点:
- 编程复杂度高
适用场景:
- 用户需求经常变化
- 互联网应用
- 游戏开发
- 企业内部应用等等
面向对象的几个核心特性如下:
- 对象
- 类
对象的魔法:
- Polymorphism 多态
- Encapsulation 封装
- Inheritance 继承
在这里我们先将继承,后续再讲多态和封装
继承 :
继承指的是类与类之间的关系,继承的功能之一就是用来解决代码重用问题。
继承是一种创建新类的方式,
class A:
def __ionit__(self):
pass
class B(A): # B 继承 A
pass
类:
类表示一组(或一类)对象 ,而每个对象都属于特定的类。类的主要任务是定义其实例将包含的属性方法,即,类是特征与技能的结合体!
类的创建:
class Name_class(object): # 定义 类
def __init__(self): # 构造函数(初始化函数)
pass
新式类 & 旧式类
类有两种写法:旧式类和新式类。
在Pyhton3之前,默认创建的是旧式类,之后就没有旧式类了,因为没人用
__metaclass__ = type # 旧式类写法,Python 2 必须包含此行代码。Pyhton3之后不用写
class P:
def __init__(self):
pass
class P(object): #新式类写法
def __init__(self):
pass
新式类和旧式类的区别:
1.只有在python2中才分新式类和经典类,python3中统一都是新式类
2.在python2中,没有显式的继承object类的类,以及该类的子类,都是经典类
3.在python2中,显式地声明继承object的类,以及该类的子类,都是新式类
4.在python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式类
♥ 构造函数(初始化函数):__init__
构造函数用于对初始化新建对象的状态,所以又称为 “初始化函数 ”。构造函数不同于普通函数在于,在对象创建之后自动调用它
1 class Foobar:
2 def __init__(self,value= 38): # 构造函数
3 self.somevar = value
4
5 f= Foobar()
6 print(f.somevar)
7
8 res:
9 38
上述代码块,实例 f为何没有传参数?因为构造函数中有默认参数 value=38,那如果你给实例中传进去参数呢?
1 f = Foobar('This is a constructor argment')
2 print(f.somevar)
3
4 res:
5
6 This is a constructor argment
如上所见,由于类的构造函数中有默认参数,当你再传进一个参数后这相当于再次赋值,So, value就被替换掉了
类的对象(object):
一个对象即是一个类的实例化后实例,一个类必须经过实例化后方可在程序中调用,一个类可以实例化多个对象,每个对象亦可以有不同的属性,就像人类是指所有人,每个人是指具体的对象,人与人之前有共性,亦有不同
对象有属性和方法组成。属性是属于对象的变量,而方法是存储在属性中的函数。
相比于其他函数,(关联的)方法有一个不同之处,那就是它总是将其所属的对象作为第一个参数,而这个参数通常被命名为 ” self ”。
1 class A(object):
2 def __init__(self): # self 为类的实例化
3
4 pass
那类是怎么实例化的呢?我们来看下以下代码:
class People:
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex=sex
r1= People('风清扬',18,'man') # r1就是类People实例化对象
实例化分解为以下过程:
1. 对类People创建一个空对象r1(现在r1就是一个空的名称空间,也就是一个空字典),
class People:
pass
r1 =People()
print(r1.__dict__) # 访问 r1 的名称空间
>>>: {}
2. 将对象 r1 和参数传进类People,再由构造函数__init__对 对象r1 进行初始化特征,将特征以字典的形式传进对象r1的名称空间内
class People:
def __init__(self,name,age,sex): # 1. __init__(r1,'风清扬',18,'man')
self.name=name
self.age=age
self.sex=sex
# 2.
# r1.name = '风清扬'
# r1.age = 18
# r1.age = 'man'
r1= People('风清扬',18,'man') # 相当于 People.__init__(r1,'风清扬',18,'man')
print(r1.__dict__)
>>>: {'name': '风清扬', 'age': 18, 'sex': 'man'}
那对于对象的属性是不是也能进行增删改查呢?答案是肯定的呀!
# 查
print(r1.name) # 风清扬
print(r1.age) # 18
print(r1.sex) # man
# 增
r1.country = 'china'
print(r1.__dict__) # {'name': '风清扬', 'age': 18, 'sex': 'man', 'country': 'china'}
# 删
del r1.country
print(r1.__dict__) # {'name': '风清扬', 'age': 18, 'sex': 'man'}
# 改
r1.age =20
print(r1.__dict__) # {'name': '风清扬', 'age': 20, 'sex': 'man'}
类的属性:
数据属性:类中定义的变量
函数属性:类中定义的函数
1 class People():
2 country = 'Chain' #数据属性
3 def func(self): # 函数属性
4 pass
查看类的名称空间:
print(People.__dict__)
>>{'__module__': '__main__', 'country': 'Chain', 'func': <function People.func at 0x000002A1404929D8>, '__dict__': <attribute '__dict__' of 'People' objects>,
'__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None}
数据属性的增删改查:
# 查 以下两种写法都可
print(People.country)
print(People.__dict__['country'])
# 增
People.name = 'lala'
print(People.name) # lala 将会以字典的形式增加到类的名称空间末尾
# 删
del People.name
# 改
People.test='haha'
print(People.test) # haha
绑定方法:
对于类的两大属性:
数据属性:类中定义的变量,供所有对象使用
class People:
country ='china'
def __init__(self,name,age,sex): # 1. __init__(r1,'风清扬',18,'man')
self.name=name
self.age=age
self.sex=sex
r1= People('风清扬',18,'man') # 相当于 People.__init__(r1,'风清扬',18,'man')
r2 =People('萧炎',18,'man')
r3 =People('林采儿',18,'man')
# 三个对象都调用类的数据属性country
print(People.country,id(People.country))
print(r1.country,id(r1.country))
print(r2.country,id(r2.country))
print(r3.country,id(r3.country))
输出:
china 2582506240520
china 2582506240520
china 2582506240520
china 2582506240520
由上述结果可见,类的数据属性类和对象均能调用。不管哪个对象调用类的数据属性,其内存地址均相同。由此可知类的数据属性是所有对象共有的
函数属性:类中定义的函数,供绑定对象使用,称为绑定到对象的方法。谁调用此方法就会把谁当做第一个参数传进方法内
# 类调用函数属性
print(People.learn())
输出
TypeError: learn() missing 1 required positional argument: 'self'
# 看下此时learn是函数还是属性
print(People.learn)
输出
<function People.learn at 0x0000014E0B4F2A60>
print(r1.learn())
print(r2.learn())
print(r3.learn())
输出:
风清扬 is learning
萧炎 is learning
林采儿 is learning
由上述结果可见:类的函数属性类和对象也都能调用
- 但当类调用时,此时函数还是函数,所以必须按照函数调用传参的规定,有几个参数就传几个
- 当对象调用时,此时函数绑定对象为方法,由于函数参数为self,所以会自动传值,即将对象本身作为参数传进函数里
类型即类
python中一切皆为对象,且python3中类与类型是一个概念,类型就是类
1 print(list)
2 >>><class 'list'> # 类型即类
3
4 # 实例化类list
5 l1=list()
6 l2=list()
7 l3=list()
8
9 # 均有方法append 相同的功能 但内存地址不同
10 print(l1.append) <built-in method append of list object at 0x000002BD3300BD88>
11 print(l2.append) <built-in method append of list object at 0x000002BD3300BDC8>
12 print(l3.append) <built-in method append of list object at 0x000002BD3300BE08>
13
14 # 操作绑定方法 传给谁谁才能调用
15 l1.append(0)
16 print(l1) # [0]
17 print(l2) # []
18 print(l3) # []