python_面向对象

  • 对象
    对象是指现实中的物体或实体

  • 面向对象
    把一切堪称对象(实例),让对象和对象之间简历关联关系

  • 对象都有什么特征:
    属性(名词):姓名,年龄,性别,颜色… … …–>变量
    行为(动作):学习,吃饭,睡觉,踢球… … …–>方法

  • 什么是类:
    用于相同属性和行为的对象分为一组,即为一个类
    类是用来描述对象的工具,用类可以创建同类对象

  • Python类名最好使用驼峰命名法
    Mylist MyRange 大驼峰(所有单词首字母大写)
    getStudentAge 小驼峰 (第一个首字母小写,其余大写)
    说明:因为python内置类为小写,防止命名冲突

  • 类的创建语句:

语法:
	class 类名(继承列表):
		'''类的文档字符串'''
		实例方法定义(类内的函数称为方法method)
		类变量定义
		类方法定义
		静态方法定义
  • 作用:
    创建一个类
    用于描述此类对象的行为和属性
    类用于创建此类的一个或多个对象(实例)
  • 类和对象
类		|	对象		实例
class	|	object		instance
  • 构造函数
表达式:
	类型([创建传参列表])
作用:
	创建这个类的实例对象,并返回此实例对象的引用关系
  • 实例(对象)说明
    实例有自己的作用域和名字空间,可以为该实例添加实例变量(属性)
    实例可以调用类方法和实例方法
    实例可以访问类变量和实例变量

示例:

class Dog:		#Dog是一个变量,绑定一个类
	pass
dog = Dog()		#dog绑定一个对象

实例方法:

语法:
	class 类名(继承列表):
		def 实例方法名(self,参数1,参数2,...):
			'''实例方法的文档字符串'''
			语句块
  • 作用:
    用于描述一个对象的行为,让此类型的全部对象都拥有相同的行为
  • 说明:
    实例方法实质是函数,是定义在类内的函数
    实例方法至少有一个形参,第一个形参代表调用这个方法的实例,一般命名为’self’
  • 实例方法的调用语法:
实例.实例方法名(调用传参)
或
类名.实例方法名(实例,调用传参)
  • 属性 attribute(也叫实例变量)
    每个实例都可以有自己的变量,此变量称为实例变量(也叫属性)
  • 属性的使用语法:
    实例.属性名
  • 赋值规则:
    首次为属性赋值则创建此属性
    再次为属性赋值必变更属性的绑定关系
  • 作用:
    用来记录对象自身的数据

实例:

#此实例用来定义一个学生类,并实现添加信息和打印信息的功能
class Student:
	def set_info(self,name,age):
		'''此方法用来给对象添加姓名和属性'''
		self.name = name
		self.age = age
	def shou_info(self):
		print(self.name,'今年',self.age,'岁')

#定义一个Student对象并调用方法
s1 = Student()
s1.set_info('小王',24)
s1.show_info()
  • 删除属性
    用del语句可以删除一个对象的实例变量
语法:del 对象.实例变量名

示例:

c1 = Cat()
c1.color = '白色'		#添加属性
print(c1.color)		
del c1.color			#删除属性
print(c1.color)			#属性错误
  • 初始化方法:
    作用:对新创建的对象添加实例变量(属性)或相应的资源
语法格式:
	class 类名(继承列表):
		def __init__(self[,形参列表]):
			语句块
说明:
	1.初始化方法名必须为__init__不可改变
	2.初始化方法会在构造函数创建实例后自动调用,且将实例自身
通过第一个参数self传入__init__方法
	3.初始化方法内部如果需要返回则只能返回None

示例:

#此示例示意__init__方法的自动调用及添加实例变量
def Car:
	def __init__(self,c,b,m):
		print('__init__方法被调用')
		self.color = c	#颜色
		self.brand = b	#品牌
		self.model = m	#型号

a4 = Car('黑色','奥迪','A4')
a4.__init__('黑色','奥迪','A4')	#显式调用

#__init__方法具体步骤:
#1.创建一个没有属性的对象
#2.把这个对象传递给__init__方法
#3.引用关系传递给A4
  • 析构方法
语法:
	class 类名(继承列表):
		def __del__(self):
			语句块
  • 说明:
    析构方法在对象销毁时被自动调用
  • 作用:清理此对象所占用的资源

示例:

class Car:
	def __init__(self,name):
		self.name = name
		print('汽车',name,'对象已创建')
	def __del__(self):
		print(self.name,'对象已被销毁')
car1 = Car('奔驰 E6')
#程序结束会自动调用__del__方法
  • 注意:python不建议在析构方法内做任何事情,因为对象销毁的时间难以确定
  • 预置实例属性
__dict__属性
	此属性绑定一个存储此实例自身变量(属性)的字典

示例:

class Dog:
	pass
dog1 = Dog()
print(dog1.__dict__)			#{}
dog1.color = '白色'
print(dog1.__dict__)			#{'color':'白色'}

#键值对赋值
dog1.__dict__['age'] = 3
__class__属性
	此属性用来绑定创建此实例的类
	作用:
		可以借助此属性来访问创建此实例的类
dog2 = dog1.__class__()	#等同于dog2 = Dog()
  • 用于类的函数:
isinstance(obj,class_or_tuple)	
#返回这个对象obj是否某个类class或某些类的实例,如果时则返回
#True,否则返回False

#实例:
animal  = Dog()
isinstance(animal,(Cat,int))		#返回False
isinstance(aninal,(Dog,int))		#返回True

Type(obj)							#返回对象的类型
  • 类变量(class variable)
    类变量是类的属性,此属性属于类
  • 作用:
    用来记录类相关的数据
  • 说明:
    类变量可以通过类直接访问(所有的对象所共有)
    类变量可以通过类的实例直接访问
    类变量可以通过此类实例的__class__属性间接访问

示例:

#此示例示意类变量的定义和使用
class Human:
	count = 0
print('Human的类变量count=',Human.count)	#0
Human.count = 100
print(Human.count)							#100

#以下示意类变量与示例变量的区别及示例间接访问类变量
h1 = Human()
h1.count = 200
print(h1.count)								#200
print(Human.count)							#100
h1.__class__.count +=200	
print(h1.count)								#200
print(Human.count)							#300
  • 类变量的应用案例:
#用类变量记录对象的个数
class Car:
	count = 0			#创建类变量,用来记录汽车对象总数
	def __init__(self,info):
		print(info,'被创建')
		self.data = info
		self.__class__.count+=1			#让汽车的总数加1
	def __del__(self):
		print(self.data,'被销毁')
		#当车被销毁时总数自动减1
		self.__class__.count -= 1
print('当前汽车总数是:',Car.count)
b1 = Car('BYD 京A.88888')
print('当前汽车总数是:',Car.count)
b2  = Car('TESLA 京B.88888')
print('当前汽车总数是:',Car.count)
del b1
print('当前汽车总数是:',Car.count)
  • 类的文档字符串
    类内第一个没有赋值给任何变量的字符串是类的文档字符串

  • 说明:
    类的文档字符串用类的__doc__属性可以访问
    类的文档字符串可以用help()函数访问

  • 类的__slots__列表
    作用:限定一个类的实例只能有固定的属性(实例变量)
    通常为当错写属性名时不添加属性而是发出报告

  • 说明:
    含有__slots__列表的类创建的实例对象没有__dict__属性,
    即此实例不用字典来保存对象的属性(实例变量)(因为字典为可变的而__slots__限制了不可变)

示例:

#此示例示意类的变量__slots__列表的作用
class Student:
	__slots__ = ['name','score']	#限制类只拥有这两个属性
	def __init__(self,name,score):
		self.name = name
		self.score = score
s1 = Student('小李',68)
print(s1.score)
s1.scrce = 100	#此处错写了属性名,若无__slots__列表将不会报错
print(s1.score)				
print(s1.__dict__)#发生AttributeError,此对象没有__dict__属性
  • 类方法@classmethod
    类方法是描述类的行为的方法,类方法属于类
  • 说明:
    类方法需要用@classmethod装饰器定义
    类方法至少有一个形参,第一个形参用于绑定类,约定写为’cls’
    类和该类的实例都可以调用类方法
    类方法不能访问此类创建的实例的属性(只能访问类变量)

示例:

#此示例示意类方法的使用
class Car:
	count = 0		#类变量

	@classmethod
	def getTotalCount(cls):		#传参业内规定写cls
		'''此方法为类方法,得到类变量'''
		return cls.count

	@classmethod
	def updateCount(cls,number):
		'''此方法为类方法,更新类变量'''
		cls.count+=number

print(Car.getTotalCount())		#用类来调用类方法
#Car.count +=1				#面向对象思想不提倡直接操作属性
Car.updateCount(1)
print(Car.getTotalCount())	#1

c1 = Car()					#创建一个对象
c1.updateCount(100)		#Car类的示例也可以调用类方法 实际上
						#相当于把c1.__class__传入cls参数
print(c1.getTotalCount())	#101

c2 = Car()
c2.count  = 200				#实例属性
print(c2.getTotalCount())	#200,类方法不能访问实例的属性
  • 总结:
    以上分别介绍了类变量、类方法
    实例变量、实例方法
    此外还有不属于类和实例的静态方法
  • 静态方法@staticmethod
    静态方法不属于类,也不属于类的实例,它相当于定义在类内的普通函数,只是它的作用域属于类

示例:

#此示例示意静态方法的创建和使用
class A:
	@staticmethod
	def myadd(a,b):
		'''此方法为静态方法
		此方法的形参不需要传入类和实例'''
		return a+b
print('1+2 = ',A.myadd(1,2))	#类调用

a = A()							#对象调用
print('100+200 = ',a.myadd(100,200))
  • 继承inheritance 和 派生derived
    (1)继承是指从已有的类中衍生出新类,新类具有原类的行为,并能扩展新的行为
    (2)派生就是从一个已有类中衍生(创建)新类,在新类上可以添加新的属性和行为
  • 继承和派生的目的:
    继承是延续旧类的功能
    派生是为了在旧类的基础上添加新的功能
  • 作用:
    (1)用继承派生机制,可以将一些共有功能加在基类中,实现代码的共享
    (2)在不改变基类的基础上改变原有功能
  • 继承/派生的名词
    基类(base class),超类(super),父类(father class)
    派生类(derived class)/子类(child class)
  • 单继承
    语法:
class 类名(基类名):
	语句块

说明:单继承是指派生类由一个基类衍生出来的类

示例:

#此示例用来示意继承和派生
class Human:
	'''此类用来描述人类的共性行为'''
	def say(self,word):
		print('say',word)
	def walk(self,distance):
		print('走了',distance,'公里')

class Student(Human):
	'''学生类单继承Human类并派生新的方法'''
	def study(self,subject):
		print('正在学习',subject)

s1 = Student()
s1.say('今天天气真好')
s1.walk(5)
s1.study('Python')
  • 继承说明:
    任何类都直接或间接的继承自object类
    object类是一切类的超类(组类,亚当夏娃级的类)
    例如:class Human(): #也可写成class Human(object):
  • 类的__base__属性:
    __base__属性用来记录此类的基类

示例:

class Human:
	pass
class Student(Human):
	pass
class Teacher(Student):
	pass

t1 = Teacher()
t1.__base__		#Student
t1.__base__.__base__	#Human
Human.__base__	#object
object.__base__		#None

#示意bool的基类
True.__base__	#bool
True.__base__.__base__		#int
int.__base__	#object 
  • 覆盖 override
    覆盖是指在有继承关系的类中,子类中实现了与基类同名的方法,在子类实例调用该方法时,实例调用的是子类中的覆盖版本的方法,这种现象叫做覆盖

示例:

class A:
	def work(self):
		print('A.work()被调用')
class B(A):
	def work(self):
		print('B.work被调用')		#覆盖父类方法

b = B()
b.work()
b.__class__.__base__.work(b)			#虽然覆盖子类也能调用父类的方法
  • 子类对象显式调用基类方法的方式:
    基类名.方法名(实例,实际调用传参)
  • super函数
super(type,obj)返回绑定超类的实例
super()	返回绑定超类的实例,等同于:(必须在子类方法内调用)
					super(__class__,实例方法的第一个参数)		

示例:

class A:
	def work(self):
		print('A.work被调用')
class B(A):
	def work(self):
		print('B.work被调用')
	def super_work(self):
		#super(B,self).work()		#A.work()
		#super(__class__,self).work()	#A.work()
		super().work()			#A.work()必须在方法内调用

b = B()
b.super_work()			#A.work()
  • 显式调用基类的初始化方法:
    当子类中实现了__init__方法时,基类的__init__方法并不会被自动调用,此时需要使用super显式调用

示例:

class Human:
	def __init__(self,n,a):
		self.name = n
		self.age = a
	def info():
		print('姓名:',self.name)
		print('年龄:',self.age)
class Student(Human):
	def __init__(self,n,a,s):
		super().__init__(n,a)#使用super显式调用基类初始化方法
		self.score = s
	def info(self):
		super().info()
		print('成绩:',self.score)

s1 = Student('小王',23,100)
s1.info()
  • 用于类的函数:
isinstance(obj,cls)
判断对象obj是否是cls的对象,若cls是obj的类或父类则返回True
否则返回False

issubclass(cls,class_or_tuple)
判断一个类cls是否继承自class_or_tuple,返回True或False

查看python内建类继承关系的方法:
help(__builtins__ )

示例:

class A:
	pass
class B(A):
	pass
class C(B):
	pass
issubclass(C,(A,B))		#True
issubclass(C,object)	#Ture
  • 封装 enclosure
    封装是指隐藏类的实现细节,让使用者不用关心这些细节
    封装的目的是让使用者尽可能少的使用实例变量进行操作
  • 私有属性:
    python类中,以双下划线’__'开头,不以双下划线结尾的标识符为私有成员,在类的外部无法直接访问

示例:

class A:
	def __init__(self):
		self.__p1 = 100		#__p1为私有属性,在外部不可访问
	def test(self):
		print(__p1)
		self.__m1()			#A类的方法可以调用A类的私有方法
	def __m1(self):			#__m1为私有方法
		print('m1方法被调用')

a = A()
print(a.__p1)		#在类外看不到__p1属性,访问失败!
a.test()			#使用方法访问私有属性
a.__m1()			#调用失败,外部无法调用私有方法,且只有
					#A类才能调用
  • 多态 polymorphic
    字面意思:“多种状态”
    多态是指在继承/派生关系的类中调用基类对象的方法。实际上,能调用子类覆盖版本方法的现象叫多态
  • 说明:
    多态调用的方法与对象无关,不与类型相关
    python的全部对象都只有‘运行时状态(动态)’,没有‘C++/Java’里的’编译时状态(静态)’

示例:

class Shape:
	def draw(self):
		print('Shape.draw被调用')
class Point(Shape):
	def draw(self):
		print('正在画一个点')
class Circle(Point):
	def draw(self):
		print('正在画一个圆')

def my_draw(s):			#不是C++/java必须要先指定s的类型
	s.draw()			#此处显示多态中的动态

s1 = Point()
s2 = Circle()
my_draw(s1)
my_draw(s2)
  • 面向对象的编程语言的特征
    继承
    封装
    多态
    如:C++/ Java/ Python/ Swift/ C#
  • 多继承 multiple inheritance
    多继承是指一个子类继承自两个或两个以上的基类
语法:
 	class 类名(基类名1,基类名2,........):
 		语句块
  • 说明:
    (1)一个子类同时继承自多个父类,父类中的方法可以同时被继承下来
    (2)如果两个父类中由同名的方法,而在子类中又没有覆盖此方法时,调用结果难以确定
  • 多继承的缺陷:
    标识符(名字空间冲突的问题)
    要谨慎使用多继承

示例:

class Car:
	def run(self,speed):
		print('汽车正在以',speed,’的速度行驶')
	def test(self):		#演示名字冲突
		print('Car.test被调用’)

class Plane:
	def fly(self,height):
		print('飞机正在海拔',height,'的高度飞行')
	def test(self):
		print('Plane.test被调用')

class CarPlane(Car,Plane):		#飞车继承飞机和汽车类
	pass

cp = CarPlane()
cp.fly(10000)			#调用成功
cp.run(100)				#调用成功
cp.test()				#CarPlane中没有覆盖,父类名称冲突
			#会调用继承列表(Car,Plane)中的第一个,这里是Car
			#之后会类似广度优先的遍历(C3算法)-->python3

在这里插入图片描述

  • 继承的MRO(Method Resolution Order)问题
    类内的__mro__属性用来记录继承方法的查找顺序

示例:

#此示例用来演示类的__mro__属性
class A:
	def m(self):
		print('A.m')

class B(A):
	def m(self):
		print('B.m')
		super().m()	#调用C.m不是A,因为继承调用是按mro顺序

class C(A):
	def m(self):
		print('C.m')

class D(B,C):
	def m(self):
		print('D.m')
		super().m()					#调用B.m

d = D()
print(D.__mro__)					#D,B,C,A,object

总结:单继承中super一定调用继承列表内的父类
而多继承中的super不一定是父类,按照mro的广度优先列表\

练习:

#自定义Mylist类继承自list类并添加insert_head功能
class Mylist(list):
	def insert_head(self,value):
		self.insert(0,value)	#可以直接使用父类的方法

L = Mylist(range(1,5))		#直接使用父类的初始化方法
print(L)
L.insert_head(0)
print(L)
L.append(5)
print(L)
  • 函数重写 override
    重写是在自定义的类中添加相应的方法,让自定义的类生成的对象(实例)像内建对象一样进行内建的函数操作
  • 对象转字符串函数重写:
repr(obj)	返回一个能代表此对象的表达式字符串,通常:
			eval(repr(obj)) == obj

str(obj)	通过给定的对象返回一个字符串(这个字符串通常是
			给人看的,没有字符两边的引号)

例如:
s = 'I am a Human'
s1 = repr(s)		#eval(s1)返回s,能完全代表s的字面值
s2 = str(s)
print(s1)			#'I am a Human' 是表达式
print(s2)			#I am a Human

示例:

class MyNumber:
	def __len__(self):
		return 100

n1 = MyNumber()
x = len(n1)			#重写了__len__方法才能这样调用
print('x = ',x)
  • repr和str的重写:
repr()函数的重写方法:
	def __repr__(self):
		return 能够表达self内容的字符串

str()函数的重写方法:
	def __str__(self):
		return 人能看懂的字符串

说明:
(1)str(obj)函数优先调用obj.__str__()方法返回字符串
(2)如果obj没有__str__()方法,则调用obj.__repr__()方法返回
的字符串
(3)如果obj没有__repr__()方法,则调用object类的__repr__()实
例方法显示<xxxx>格式的字符串
(4)__str__ 在用户调用print打印对象时,该方法会自动执行。
(5)__repr__ 在机器(在python命令行中实例化对象后,再只写个对象名
回车)使用时自动执行。

注意:在没有写__str__方法的时候,且写了repr方法时。__str__= 
__repr__

示例:



#此示例演示重写__repr__与__str__方法
class Student:
	def __init__(self,name):
		self.__name = name
	def __str__(self):				
		return str(self.__name)	
	def __repr__(self):
		return repr(self.__name)
	

s1 = Student('小王')
print(s1)					#小王,优先调用__str__,
					#若没有__str__而有__repr__则是'小王'
  • 其余函数的重写只需在类内加前后双下划线即可
    如内建函数的重写:
__abs__					__int__
__complex__				__float__
__reversed__			__bool__
__round__				__len__
  • 布尔测试函数的重写
格式:
	def __bool__(self):
		...
作用:
	用于bool(obj)函数取值
	用于if语句真值表达式中(若有__bool__方法,if obj会自动调用
					并判断,否则__len__判断之后无条件为真)
	
	用于while语句真值表达式中
说明:
	1.优先调用__bool__()方法取值
	2.如果不存在__bool__()方法,则调用__len__()方法去之后判断
是否为0值,不为0返回True,否则返回False
	3.如果再没有__len__()方法,则直接返回True

示例:

#此示例示意bool函数的重写与len函数重写的关系
class Mylist:
	def __init__(self,iterator = []):
		self.data = [x for x in iterator]

	#def __bool__(self):
	#	return bool(self.data)
	
	#def __len__(self):			#当没有bool重写时使用len重写
								#的结果判断
	
	#	return len(self.data)	#若同时没有len重写,则无条件
								#返回True

l = Mylist([])
if l:
	print('Ture')
else:
	print('False')
  • 迭代器(高级)
    什么是迭代器:可以通过next(it)函数取值的对象
    迭代器协议:
    迭代器协议是指对象能够使用next函数获取下一项数据,在没有下一项数据时触发一个StopIterator来终止迭代的约定
    实现方法:
    类内需要有__next__(self)方法来实现迭代器协议
语法形式:
class MyIterator:
	def __next__(self):
		迭代器协议的实现
		return 数据
  • 什么是可迭代对象:
    是指能用iter(obj)函数返回迭代器的对象(实例)
    可迭代对象内部一定要定义__iter__(self)方法来返回迭代器
  • 什么是迭代器:
    类用用于__next__方法的叫做迭代器
    __next__方法必须符合迭代器协议
  • 可迭代对象的语法形式:
class MyIterable:
	def __iter__(self):
		语句块
		return 迭代器			#必须返回迭代器

练习:
在这里插入图片描述

class Fibonacci:
	def __init__(self,n):
		'''此方法用于初始化Fib数列'''
		self.__count = n
		if n==1:
			self.__it = [1]
		elif n==2:
			self.__it = [1,1]
		elif n>2:
			tmp = 2
			self.__it = [1,1]
			while tmp<=n:
				self.__it.append(self.__it[tmp-1]+self.__it[tmp-2])
				tmp+=1
	
	def __iter__(self):
		'''将Fib作为可迭代对象,必须返回迭代器'''
		return MyIterator(self.__it)	#此处也可以使用内建的迭代器
										#如return iter(self.__it)
	def __repr__(self):
		'''向Fib数列提供打印功能'''
		return '%r' % self.__it
	

class MyIterator:
	'''自建迭代器(拥有__next__方法且方法返回值才算迭代器)'''
	def __init__(self,iterable):
		self.__cur = 0
		self.__it = iterable
	def __next__(self):
	'''迭代器协议'''		
		if self.__cur >=len(self.__it):
			raise StopIteration
		self.__r = self.__it[self.__cur]
		self.__cur+=1
		return self.__r
	
	def __str__(self):
		'''向迭代器提供打印功能'''
		return str(self.__r)

for x in Fibonacci(10):
	print(x)
L = [x for x in Fibonacci(30)]
print(L)
print(sum(Fibonacci(25)))

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值