目录
Python 系列文章学习记录:
Python系列之Windows环境安装配置_开着拖拉机回家的博客-CSDN博客
Python系列之变量和运算符_开着拖拉机回家的博客-CSDN博客
Python系列之判断和循环_开着拖拉机回家的博客-CSDN博客
Python系列之字符串和列表_开着拖拉机回家的博客-CSDN博客
Python系列之文件操作和函数_开着拖拉机回家的博客-CSDN博客
Python系列模块之标准库OS详解_开着拖拉机回家的博客-CSDN博客
Python系列模块之标准库re详解_开着拖拉机回家的博客-CSDN博客
Python系列模块之标准库json详解_开着拖拉机回家的博客-CSDN博客
Python系列模块之标准库shutil详解_开着拖拉机回家的博客-CSDN博客
Python系列模块之pymysql操作MySQL 数据库_开着拖拉机回家的博客-CSDN博客
一、面向对象编程
1.1 面向对象三大特征
面向对象编程的三大特征:封装,集成,多态。
封装:客观的事物封装成类(将数据和方法放在一个类中就构成了封装)
继承:在Python中一个类可以集成于另一个类也可以继承多个类,被继承的类叫父类(或者叫基类,base class),继承的类叫子类
多态(polymorphism):指的是一类事物有多种形态,一个抽象类有多个子类(因而多态的概念依赖于继承),不同的子类对象调用相同的方法,产生不同的执行结果,多态可以增加代码的灵活度
- 面向对象编程(Object Oriented Programming),简称OOP,是一种程序设计思想,OOP把对象当作程序的基本单元,一个对象包含了数据和操作数据的函数。
- 面向对象的程序设计把计算机程序当作一组对象的集合,每个对象都可以接受其他对象发来的消息,并且进行处理,而计算机程序的执行就是一系列消息在各对象之间进行传递,和面向对象不同,面向过程的程序设计把计算机程序当作一系列的命令集合,即一组函数的顺序执行,为了简化程序设计,面向过程把函数继续切分为子函数,从而降低系统的复杂度。
- 在Python中,所有的数据类型都可以看作为对象,当然也可以自定义对象,自定义的对象数据类型就是面向对象中类(class)的概念,下面来看一个案例来说明面向对象和面向过程的区别:
实例:
stu01 = {'name': "张三丰", 'age': "110", 'address': "上海"}
stu02 = {'name': "张无忌", 'age': "200", 'address': "西安"}
stu03 = {'name': "张无忌", 'age': "200", 'address': "北京"}
def print_info(stu):
return print('%s : %s : %s' % (stu['name'], stu['age'], stu['address']))
print_info(stu01)
print_info(stu02)
print_info(stu03)
print("++++++++++++++++")
# 面向对象
class Person(object):
def __init__(self, name, age, address):
self.name = name
self.age = age
self.address = address
def getInfo(self):
print(self.name, self.age, self.address)
p01 = Person("张三丰", 110, "北京")
p02 = Person("张启山", 110, "西安")
p01.getInfo()
p02.getInfo()
执行结果:
1.2 什么是对象
对象(class)是一种抽象的概念,上面定义的对象Person,指的就是学生这个概念,而实例(instance)则指一个个具体的对象,例如上面的张三丰和l张无忌就是两个具体的Person,也就是实例
从上面的案例可以看出,面向对象的程序设计思想其实就是抽象出对象(class),然后根据对象创建实例(instance)
最后,面向对象的抽象程度比函数高,因为一个对象既包含数据,也包含操作数据的方法,封装、继承、多态是面向对象的三大特点。
二、类(class)和实例(instance)
类是总结事物特征的抽象概念,是创建对象的模板。对象是按照类来具体化的实物。是总结事物特征的抽象概念,是创建对象的模板。对象是按照类来具体化的实物。
2.1 类的构成
- 类的名称: 类名
- 类的属性: 一组参数数据
- 类的方法: 操作的方式或行为
面向对象最重要的概念就是类(class)和实例(instance),类是抽象的模板,比如上面的Person类,而实例是根据类创建出来的具体的对象,每个对象都有相同的方法,但是各自的数据可能不同,例如上面的张三丰和l张无忌。
2.2 创建类
使用 class 语句来创建一个新类,class 之后为类的名称并以冒号结尾:
class ClassName:
'类的帮助信息' #类文档字符串
class_suite #类体
以Person类为例
# 面向对象
class Person(object):
personCnt = 0
def __init__(self, name, age, address):
self.name = name
self.age = age
self.address = address
def getInfo(self):
print(self.name, self.age, self.address)
def displayCount(self):
print("类的属性:", self.personCnt)
在Python中,类是通过'class'关键字进行定义的,'class'后面跟着的是类名,
类名通常是以大写字母开头的,紧接着就是'(object)',
这个表示的是'Person'类是从'object'类继承下来的。
-
方法__init__()方法是一种特殊的方法,被称为类的构造函数或初始化方法,当创建了这个类的实例时就会调用该方法 self 代表类的实例,self 在定义类的方法时是必须有的,虽然在调用时不必传入相应的参数。
- personCnt变量是一个类变量,它的值将在这个类的所有实例之间共享。你可以在内部类或外部类使用 Person.personCnt 访问。
self代表类的实例,而非类
类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self。
class ObjTest:
def print_info(self):
print("当前对象地址: ", self)
print("指向类: " , self.__class__)
objTest = ObjTest()
objTest.print_info()
objTest1 = ObjTest()
objTest1.print_info()
执行结果:
从执行结果可以很明显的看出,self 代表的是类的实例,代表当前对象的地址,而 self.__class__ 则指向类。
self 不是 python 关键字,我们把他换成 runoob 也是可以正常执行的:
class ObjTest:
def print_info(runoob):
print("当前对象地址: ", runoob)
print("指向类 : " , runoob.__class__)
objTest = ObjTest()
objTest.print_info()
objTest1 = ObjTest()
objTest1.print_info()
执行结果:
2.3 创建实例对象和访问属性
实例化类其他编程语言中一般用关键字 new,但是在 Python 中并没有这个关键字,类的实例化类似函数调用方式。
以下使用类的名称 Person 来实例化,并通过 __init__ 方法接收参数。
# 面向过程
stu01 = {'name': "张三丰", 'age': "110", 'address': "上海"}
stu02 = {'name': "张无忌", 'age': "200", 'address': "西安"}
stu03 = {'name': "张无忌", 'age': "200", 'address': "北京"}
def print_info(stu):
return print('%s : %s : %s' % (stu['name'], stu['age'], stu['address']))
print_info(stu01)
print_info(stu02)
print_info(stu03)
print("++++++++++++++++")
# 面向对象
class Person(object):
personCnt = 0
def __init__(self, name, age, address):
self.name = name
self.age = age
self.address = address
# 使用点号 . 来访问对象的属性
Person.personCnt += 1
def getInfo(self):
print(self.name, self.age, self.address)
def displayCount(self):
print("类的属性:", self.personCnt)
# 创建 Person 类的第一个对象
p01 = Person("张三丰", 110, "北京")
# 创建 Person 类的第二个对象
p02 = Person("张启山", 110, "西安")
p01.getInfo()
p02.getInfo()
p01.displayCount()
p01.personCnt = 5
print("类的属性personCnt修改: ", p01.personCnt)
del p01.personCnt
print("删除类的属性personCnt: ", p01.personCnt)
执行结果:
可以使用以下函数的方式来访问属性:
- getattr(obj, name[, default]) : 访问对象的属性。
- hasattr(obj,name) : 检查是否存在一个属性。
- setattr(obj,name,value) : 设置一个属性。如果属性不存在,会创建一个新属性。
- delattr(obj, name) : 删除属性。
实例:
stu01 = {'name': "张三丰", 'age': "110", 'address': "上海"}
stu02 = {'name': "张无忌", 'age': "200", 'address': "西安"}
stu03 = {'name': "张无忌", 'age': "200", 'address': "北京"}
def print_info(stu):
return print('%s : %s : %s' % (stu['name'], stu['age'], stu['address']))
print_info(stu01)
print_info(stu02)
print_info(stu03)
print("++++++++++++++++")
# 面向对象
class Person(object):
personCnt = 0
def __init__(self, name, age, address):
self.name = name
self.age = age
self.address = address
# 使用点号 . 来访问对象的属性
Person.personCnt += 1
def getInfo(self):
print(self.name, self.age, self.address)
def displayCount(self):
print("类的属性:", self.personCnt)
# 创建 Person 类的第一个对象
p01 = Person("张三丰", 110, "北京")
getPersonCnt = getattr(p01, "personCnt")
print("getattr 类的属性personCnt : ", getPersonCnt)
hasPersonCnt = hasattr(p01, "personCnt")
hasPersonCnt01 = hasattr(p01, "personCnt01")
print("hasattr 类的属性personCnt : ", hasPersonCnt)
print("hasattr 类的属性personCnt01 : ", hasPersonCnt01)
setattr(p01, "personCnt", 8)
print("setattr 类的属性personCnt : ", p01.personCnt)
执行结果:
2.4 Python内置类属性
- __dict__ : 类的属性(包含一个字典,由类的数据属性组成)
- __doc__ :类的文档字符串
- __name__: 类名
- __module__: 类定义所在的模块(类的全名是'__main__.className',如果类位于一个导入模块mymod中,那么className.__module__ 等于 mymod)
- __bases__ : 类的所有父类构成元素(包含了一个由所有父类组成的元组)
实例:
# 面向对象
class Person(object):
"""所有人员的基类"""
personCnt = 0
def __init__(self, name, age, address):
self.name = name
self.age = age
self.address = address
# 使用点号 . 来访问对象的属性
Person.personCnt += 1
def getInfo(self):
print(self.name, self.age, self.address)
def displayCount(self):
print("类的属性:", self.personCnt)
# 创建 Person 类的第一个对象
p01 = Person("张三丰", 110, "北京")
# 创建 Person 类的第二个对象
p02 = Person("张无忌", 110, "西安")
print("类的文档字符串 Person.__doc__:", Person.__doc__)
print("类名 Person.__name__:", Person.__name__)
print("类定义所在的模块 Person.__module__:", Person.__module__)
print("类的所有父类构成元素 Person.__bases__:", Person.__bases__)
print("类的属性字典 Person.__dict__:", Person.__dict__)
执行结果:
2.5 类属性与方法
- 类的私有属性
__private_attrs:两个下划线开头,声明该属性为私有,不能在类的外部被使用或直接访问。在类内部的方法中使用时 self.__private_attrs。
- 类的方法
在类的内部,使用 def 关键字可以为类定义一个方法,与一般函数定义不同,类方法必须包含参数 self,且为第一个参数
- 类的私有方法
__private_method:两个下划线开头,声明该方法为私有方法,不能在类的外部调用。在类的内部调用 self.__private_methods
实例:
# 面向对象
class Person(object):
"""所有人员的基类"""
# 公开变量
personCnt = 0
# 私有变量
__secretPersonCnt = 0
def __init__(self, name, age, address):
self.name = name
self.age = age
self.address = address
def getInfo(self):
print("公共属性: ", self.name, self.age, self.address)
def displayCount(self):
self.__secretPersonCnt += 1
# 使用点号 . 来访问对象的属性
self.personCnt += 1
print("类的公共属性:", self.personCnt, "类的私有属性:", self.__secretPersonCnt)
# 创建 Person 类的第一个对象
p01 = Person("张三丰", 110, "北京")
# 创建 Person 类的第二个对象
p02 = Person("张无忌", 110, "西安")
p01.getInfo()
p01.displayCount()
print("类的公共属性:", p01.personCnt)
print("类的私有属性:", p01.__secretPersonCnt)
执行结果:
如果 我们使用Pycharm 编辑代码,用实例 “.” 类的私有属性是不能联想出来的。
可以使用 object._className__attrName( 对象名._类名__私有属性名 )访问属性
# 面向对象
class Person:
# 私有变量
__secretPersonCnt = "背着那书包上学堂,不怕太阳晒不怕风雨吹 "
person = Person
print("类的私有属性访问:", person._Person__secretPersonCnt)
执行结果:
三、类的继承
面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制。
通过继承创建的新类称为子类或派生类,被继承的类称为基类、父类或超类。
继承语法
class 派生类名(基类名)
...
在python中继承中的一些特点:
- 1、如果在子类中需要父类的构造方法就需要显式的调用父类的构造方法,或者不重写父类的构造方法。详细说明可查看:python 子类继承父类构造函数说明。
- 2、在调用基类的方法时,需要加上基类的类名前缀,且需要带上 self 参数变量。区别在于类中调用普通函数时并不需要带上 self 参数
- 3、Python 总是首先查找对应类型的方法,如果它不能在派生类中找到对应的方法,它才开始到基类中逐个查找。(先在本类中查找调用的方法,找不到才去基类中找)。
如果在继承元组中列了一个以上的类,那么它就被称作"多重继承" 。
派生类的声明,与他们的父类类似,继承的基类列表跟在类名之后,如下所示:
class Parent:
parentAttr = 100
def __init__(self):
print("调用父类构造函数")
def parentMethod(self):
print("调用父类方法")
def setAttr(self, attr):
Parent.parentAttr = attr
def getAttr(self):
print("父类属性:", Parent.parentAttr)
class Child(Parent):
def __init__(self):
print("调用子类的构造函数")
def childMethod(self):
print("调用子类方法")
c = Child() # 实例化子类
c.childMethod() # 调用子类方法
c.parentMethod() # 调用父类方法
c.setAttr(200) # 再次调用父类的方法 - 设置属性值
c.getAttr() # 再次调用父类的方法 - 获取属性值
执行结果:
说明:
- 上面的案例Child 就是 Parent的子类,Parent 是 Child 的父类;
- Child 继承 Parent后,可以直接使用 Parent 中的方法,这也是继承最大的好处:子类可以获得父类的全部功能
3.1 方法重写
如果你的父类方法的功能不能满足你的需求,你可以在子类重写你父类的方法:
class Parent:
def __init__(self):
print("调用父类构造函数")
def myMethod(self):
print("调用父类方法")
class Child(Parent):
parentAttr = 100
def __init__(self):
print("调用子类的构造函数")
def myMethod(self):
print("调用子类方法", Child.parentAttr + 1)
c = Child() # 实例化子类
c.myMethod() # 子类调用重写方法
执行结果:
子类对 myMethod 方法 重写,输出的是 子类的 打印信息,重写是:当子类和父类同时存在相同的方法时,在调用子类的时候,子类的方法会覆盖掉父类的方法。
四、多态
多态(polymorphism):指的是一类事物有多种形态,一个抽象类有多个子类(因而多态的概念依赖于继承),不同的子类对象调用相同的方法,产生不同的执行结果,多态可以增加代码的灵活度。类的多态特性,还要满足以下 2 个前提条件:
- 继承:多态一定是发生在子类和父类之间;
- 重写:子类重写父类的方法。
class Animal(object):
"""动物类"""
def func(self):
print('动物发出了声音')
class Cat(Animal):
"""猫类"""
def func(self):
print('猫:喵 喵 喵')
class Dog(Animal):
"""狗类"""
def func(self):
print('狗:汪 汪 汪 ')
def work(a: Animal):
a.func()
dog = Dog()
cat = Cat()
work01(dog)
work01(cat)
执行结果:
可以看到,Dog 和 Cat 都继承自 Animal 类,且各自都重写了父类的 func() 方法。从运行结果可以看出,在work()方法中, 传入 cat 和 dog 实例 执行同一个 func() 方法时,由于 a 实际表示不同的类实例对象,因此 a.func() 调用的并不是同一个类中的 func() 方法,这就是多态。
————————————————
此文参考廖雪峰官网:面向对象编程 - 廖雪峰的官方网站 (liaoxuefeng.com)
原文链接:Python(8)面向对象编程_用面向对象为电脑估价python_礁之的博客-CSDN博客