目录
一、开篇
1、什么是面向对象?
第一个问题,先来看下百度百科的解释:
面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)的主要思想是把构成问题的各个事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙一个事物在整个解决问题的步骤中的行为。面向对象程序设计中的概念主要包括:对象、类、数据抽象、继承、动态绑定、数据封装、多态性、消息传递。通过这些概念面向对象的思想得到了具体的体现。
简单理解,就是一种程序设计的思想,在程序设计中,利用对象去解决问题。
2、为什么要面向对象编程?
第二个问题,同样,再看下百度百科里的解释:
“面向过程”和“面向对象”
面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了;面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。
简单理解,面向过程做的事,就是按照业务逻辑从上到下写代码,每一步做什么,怎么实现,然后面向过程做完了,可以将功能代码封装到函数里,面向对象则关注谁来去做每一步,是更抽象的一种思想,里面将数据和函数绑定到一起,进行封装,减少了代码的重复重写。面向对象有三大基本特征:封装、多态、继承,这个在后面详细展开。
二、面向对象的基本概念
1、类和对象
1.1类是什么
类是一个模版,类里面可以包含多个函数,函数里面可以去实现一些功能。
1.2类的组成
类名+类的属性+类的方法
以下是一个类的例子
#创建一个类 (object)可省略
class Person(object):
#类的属性
name = 'LiMing'
age = 35
#类的方法 以def开头的函数
def eat(self):
print("eat")
def run(self):
print("run")
1.2.2类属性vs实例属性
如上面,name、age都是类属性
定义在类里面,实例方法外面,可通过类对象和实例对象访问。
定义在方法里面使用self引用的属性,称为实例属性,具体见__init__(self)方法,只能通过实例对象访问。
如以下代码所示:
class Biology:
LB = "type"
def __init__(self,name):
self.name = name
#类属性通过类对象访问
zw1 = Biology.LB
#类属性通过实例方法访问
zw2 = Biology("zw").LB
#实例属性只能通过实例方法访问
dw1 = Biology("dxm").name
print(zw1)
print(zw2)
print(dw1)
1.2.3类属性修改和访问
在类外修改类属性,必须通过类对象去修改类属性。
通过实例对象修改,只会产生一个同名的实例属性,改的只是实例属性,不会影响到类属性。
class Biology:
LB = "type"
def __init__(self,name):
self.name = name
#通过类对象修改类属性 再次访问时已经修改
Biology.LB = 'xm'
print(Biology.LB)
#通过实例对象修改类属性,再次访问并未修改
Biology("DXM").LB = "XXM"
print(Biology.LB)
1.2.4私有化属性
如果不想随意通过类名修改类属性,保护属性的安全,这时需要怎么做?
这时引出属性的私有化的概念。
语法:两个下划线开头,声明该属性为私有,不能在类的外部被使用或直接访问
示例如下:
class Biology:
__LB = "type" #定义一个私有化属性,属性前面加两个下划线
XM = Biology()
print(XM.__LB) #实例对象无法访问私有属性
print(Biology.__LB) #类对象无法访问私有类属性
那私有化属性能否在类里面修改、访问呢?
答案是可以的,看示例代码
输出:type lei
class Biology:
__LB = "type" #定义一个私有化属性,属性前面加两个下划线
#访问私有属性
def breathe(self):
return Biology.__LB
#修改私有属性
def xg(self):
Biology.__LB = "lei"
XM = Biology()
#打印出breathe这个实例方法访问到的私有属性的值
print(XM.breathe())
XM.xg()
#调用修改私有属性的xg实例方法后,再次访问时,私有属性值已经修改掉了
print(XM.breathe())
那在类里可以访问修改,在子类里能否访问呢?(这里涉及到继承相关,可先移步继承那块
示例如下,结果抛异常:'Animal' object has no attribute '_Animal__name'
class Biology:
# 定义一个私有化属性
def __init__(self):
__name = 'name'
class Animal(Biology):
def eat(self):
#尝试访问父类的私有实例属性
print(self.__name)
XM = Animal()
XM.eat()
1.2.5property属性函数
前面访问私有变量时,访问、修改,是写了方法去控制,能否有其他的访问方式?
python提供了属性函数:property
具体应用方式请看示例代码
第一种方式:要求方法必须以set、get开头
class Biology:
def __init__(self):
self.__breathe = 'breathe'
def set_eat(self,name):
self.__breathe = name
return self.__breathe
def get_eat(self):
return self.__breathe
#定义一个属性,这里实例方法必须以set、get开头
breathe = property(get_eat,set_eat)
XM = Biology()
XM.breathe = 'hello'
print(XM.breathe)
第二种方式:在方法上使用装饰器
@property是用于访问的,@函数名.setter是用于修改的
注意:这里访问和修改的函数名要一致。
class Biology:
def __init__(self):
self.__breathe = 'breathe'
@property #对函数run进行装饰,提供一个getter方法
def run(self):
return self.__breathe
@run.setter #使用装饰器进行装饰,提供一个setter
def run(self,name):
self.__breathe = name
return self.__breathe
XM = Biology()
# XM.run = 'hello'
print(XM.run)
1.3对象是什么?
对象是根据模版创建的实例,可以通过实例对象去执行类中的函数。
1.4对象怎么创建
对象名=类名()
例子:创建一个对象
#创建一个叫LiMing的对象
LiMing = Person()
#调用Person类里的方法
LiMing.eat()
LiMing.run()
#访问类属性
print(LiMing.age)
2、方法
2.1实例方法
使用关键字def定义,第一个行参默认传实例对象本身,一般使用self做为第一个参数。
在类的组成示例代码中,eat、run都是实例方法
2.2类方法
类对象拥有的方法,用装饰器@classmethod来标识其为类方法,第一个参数必须是类对象,一般以cls作为第一个参数,类方法可以通过类对象,实例对象调用
示例代码如下,打印结果:
type (引用的类属性)
None(因为函数无返回值)
class Biology:
LB = "type"
def __init__(self,name):
self.name = name
#类方法用classmethod来装饰
@classmethod
def breathe(cls):
print(cls.LB)
# XM = Biology("DXM")
#获取类属性
print(Biology.breathe())
2.3静态方法
类对象拥有的方法,用@staticmethod来表示静态方法,静态方法不需要参数。
代码如下,打印结果:type yes
class Biology:
LB = "type"
def __init__(self,name):
self.name = name
#静态方法,用装饰器staticmethod来装饰
@staticmethod
def breathe(): #无需传递任何参数
print(Biology.LB)
return 'yes'
# XM = Biology("DXM")
#获取类属性
print(Biology.breathe())
2.4__init__(self)方法
关于属性,可以添加类属性,也可以创建对象后,去添加属性。
创建对象时,调用类属性,则用到了__init__(self)方法
代码如下,实例化对象时,传递实参:Name,Age
self和对象指向的是同一个内存地址,self是对象的引用
class Person(object):
#初始化的方法
def __init__(self,Name,Age):
self.name = Name
self.age = Age
LiHua = Person('LiHua','40')
print(LiHua.name)
print(LiHua.age)
2.5魔术方法
__init__方法是一个魔术方法,经常在初始化类时使用,创建实例对象为其赋值
此外,还有常用的几个魔术方法
2.5.1__str__方法
注意:__str__方法只能return一个字符串
打印对象时,会执行这个方法,输出结果:LiHua的年龄是40
class Person(object):
def __init__(self,Name,Age):
self.name = Name
self.age = Age
def __str__(self):
return '{}的年龄是{}'.format(self.name,self.age)
LiHua = Person('LiHua','40')
print(LiHua)
2.5.2__del__方法
析构方法:当一个对象被删除或销毁时,python解释器也会默认调用__del__这个方法
如下代码,程序执行完成后会自动调用__del__这个方法
手动删除时,也会调用__del__这个方法
析构函数一般用于资源的回收。
class Person(object):
def __init__(self,Name,Age):
self.name = Name
self.age = Age
def __del__(self):
print('__del__方法被销毁')
LiHua = Person('LiHua','40')
print(LiHua)
2.6私有化方法
前面属性提到了私有化属性,那同理,方法能不能私有化呢?
答案是可以的,有的重要的方法,不允许外部调用,防止子类意外重写,可以把普通的方法设置成私有化方法。
私有化方法也是在方法名前加两个下划线。
示例代码如下:
class Biology:
#普通实例方法
def eat(self):
print('eat')
#私有化方法
def __breathe(self):
print('breathe')
XM = Biology()
#调用普通实例方法 可以调用成功
XM.eat()
#调用私有化方法抛出异常:'Biology' object has no attribute '__breathe'
XM.__breathe()
私有化方法也是可以内部调用的,但外部不能调用,子类也不能继承
扩展关于python命名中下划线的应用,可以看看这篇博客,写得很清晰。
三、对象的三大基本特征
1、继承
1.1单继承
子类继承父类
继承的方法,类名+(父类),父类的属性、实例方法会继承给子类
class Animal(object):
def eat(self):
print("eat")
class Person(Animal):
pass
LiMing = Person().eat()
1.2多继承
Person类可继承Biology、Animal中的方法,同时拥有Biology、Animal的属性和方法。
类名+(父类1,父类2……) 可继承多个父类
class Biology:
def breathe(self):
print('breathe')
class Animal(object):
def eat(self):
print("eat")
class Person(Animal,Biology):
pass
LiMing = Person().eat()
LiHua = Person().breathe()
有个问题,如果多个父类中有同名的实例方法,这时应该去继承哪个?继承顺序是怎么样的?
class Biology:
def breathe(self):
print('biology,breathe')
class Animal(object):
def eat(self):
print("eat")
def breathe(self):
print('Animal,breathe')
class Person(Animal,Biology):
pass
LiHua = Person().breathe()
print(Person.__mro__)
输出结果如下:
Animal,breathe
(<class '__main__.Person'>, <class '__main__.Animal'>, <class '__main__.Biology'>, <class 'object'>)
用__mro__函数查看一下,代码查找顺序为Person、Animal、Biology
在Biology中找到后就不再往后找了,是按继承时括号里的顺序进行查找的。
1.3继承传递
子类继承父类,但如果父类又继承了父类呢?子类是否能继承父类的父类呢?
看代码,结果是Person可以继承父类Animal的父类Biology的。
父类称为基类,子类称为派生类。父类的属性和实例方法可以一级一级传递下去,这就是继承的传递性。
class Biology:
def breathe(self):
print('biology,breathe')
class Animal(Biology):
def eat(self):
print("eat")
class Person(Animal):
pass
LiHua = Person().breathe()
print(Person.__mro__)
biology,breathe
(<class '__main__.Person'>, <class '__main__.Animal'>, <class '__main__.Biology'>, <class 'object'>)
1.4父类的重写
如下代码,Animal类继承了Biology类,但有重写了breathe方法,此时子类中的方法会覆盖掉父类同名的方法,执行结果为:Animal,breathe
class Biology:
def breathe(self):
print('biology,breathe')
class Animal(Biology):
def eat(self):
print("eat")
def breathe(self):
print('Animal,breathe')
dxm = Animal().breathe()
1.5调用父类的方法
三种在子类中调用父类的方法:
super(Animal,self).__init__(name) super().__init__(name) Biology.__init__(self,name)
class Biology:
def __init__(self,name):
self.name = name
class Animal(Biology):
def __init__(self,name):
super(Animal,self).__init__(name)
# super().__init__(name)
# Biology.__init__(self,name)
self.name += 'hello'
print(self.name)
dxm = Animal('xm')
2、封装
把内容封装到某个地方,便于后面的调用
需要满足:内容封装到一处,从另外一处调用被封装内容
将初始化构造方法将内容封装到对象中,然后通过对象直接或者self来获取被封装的内容
3、多态
百度百科里多态的解释:
在编程语言和类型论中,多态(英语:polymorphism)指为不同数据类型的实体提供统一的接口。
例子举的很形象:比如有动物(Animal)之类别(Class),而且由动物继承出类别鸡(Chicken)和类别狗(Dog),并对同一源自类别动物(父类)之一消息有不同的响应,如类别动物有“叫()”之动作,而类别鸡会“啼叫()”,类别狗则会“吠叫()”,则称之为多态。
简而言之,就是同一个接口,有不同的实现方式,具体可参考下面的这篇博客,写的很清楚。