第十章 面向对象

目录

一、面向对象编程

1.1 概念

1.2 函数式编程和面向对象编程的选择

1.3 创建类 

1.4 创建实例对象

1.5 访问类的实例方法或属性

1.6 类和对象在内存中是如何保存

1.7 封装和调用

1.7.1 封装

1.7.2 调用

1.8 类成员

1.8.1 字段

1.8.2 方法

1.8.3 属性

1.9 继承

1.9.1 继承的特点

1.9.2 单继承

1.9.3 多继承

1.9.4 子类调用基类方法

1.9.5 调用父类的__init__方法

1.9.6 类间关系判断

1.10 property()方法

1.11 多态

1.11.1 方法多态

1.11.2 运算符多态

1.11.3 方法重载

1.11.4 方法(运算符)重写

1.12 类的特殊成员

 1.12.1 __doc__

1.12.2 __module__ 和 __class__

1.12.3 __del__

1.12.4 __call__

1.12.5 __dict__

1.12.6 __str__

1.12.7 __getitem__、__setitem__、__delitem__

1.12.8 __getslice__、__setslice__、__delslice__

1.12.9 __iter__

1.12.10 .__slot__

1.12.11 __new__

二、课堂练习

1、在文件的第二行之后插入一行内容,且统计插入后的文件行数

2、读一个文件,包含英文句子,请统计共多少个不重复的单词,并在另外一个文件中内容每个单词及他的出现次数

3、将字典的key和value互换,生成新的字典,不考虑value重复的情况

4、网游:写个主程序,打架,随机生成多个人物,放到列表中。使用随机的方法,让其中的人物随机打架,最后打印一下所有人的血值和经验值

5、自定义一个函数,用到异常处理的各个关键字

6、统计一个目录及其子目录下的所有文件名字

7、求一个文件路径下的jpg文件的数量

8、求一个目录下所有文件.txt的文件路径

9、两个3*3的矩阵,实现其对应位置的数据相加,并返回一个矩阵


一、面向对象编程

1.1 概念

在编程领域有三种实现方式,面向过程,面向函数和面向对象,区别如下:

  • 面向过程:按照业务逻辑一步步实现响应的需求
  • 面向函数:将常用的功能封装到函数,避免重复编写代码,而且便于维护
  • 面向对象:将函数进行分类和封装,可以更快更好的开发
Python从设计之初就已经是一门面向对象的语言。
  • 类(Class):用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
  • 实例化:创建一个类的实例,类的具体对象。
  • 对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
  • 数据成员:类变量或者实例变量,用于处理类及其实例对象的相关的数据。
  • 类变量指的是在类中,但在各个类方法外定义的变量。所有类的实例化对象都同时共享类变量,类变量的调用方式有2种:1)使用类名直接调用;2)使用类的实例化对象调用
  • 实例变量指的是在任意类方法内部,以“self.变量名”的方式定义的变量,其特点是只作用于调用方法的对象。另外,实例变量只能通过对象名访问,无法通过类名访问。
  • 局部变量:直接以“变量名=值”的方式进行定义。局部变量只能用于所在类方法中,函数执行完成后,局部变量也会被销毁。
  • 方法:类中定义的函数。
  • 方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
  • 继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟"是一个(is-a)"关系(例图,Dog是一个Animal)。

1.2 函数式编程和面向对象编程的选择

python同时支持函数式编程和面向对象编程,不像java是完全面向对象。那么函数式编程和面向对象编程该如何选择呢?

比较:

  1. 函数式编程直接执行函数即可,而面向对象编程还需要创建对象,通过对象执行方法,函数式编程貌似更简单一些;
  2. 函数执行完毕后无法保存数据的状态,数据管理不方便。而面向对象编程可以将对象序列化,即使关闭程序后反序列话依然可以取出来。
  3. 函数式编程能完成的操作,面向对象都可以实现;而面向对象的能完成的操作,函数式编程不行(函数式编程无法实现面向对象的封装功能)

结论

➢函数式的应用场景 --> 各个函数之间是独立且无共用的数据

➢面向对象编程的应用场景 -->各个函数公用一组数据

所以,一般在Python开发中,全部使用面向对象面向对象和函数式混合使用

1. 多函数需使用共同的值,如:数据库的增、删、改、查操作都需要连接数据库字符串、主机名、用户名和密码
2. 需要创建多个事物,每个事物属性个数相同,但是值的需求不同。如:张三、李四、杨五,他们都有姓名、年龄、血型,但其都是不相同。即:属性个数相同,但值不相同

1.3 创建类 

使用class语句来创建一个新类,class之后为类的名称类名首字母大写,采用驼峰命名法),类名后有一括号,括号中为基类(可以有多个基类)然后以冒号结尾。

class ClassName(BaseClass1,BaseClass2,…):

'optional class documentation string'#类的帮助信息

        class_suite  #类体

以下3中方式都可以创建类: 

class Person():pass

class Person:pass

class Person(object):pass

类的帮助信息可以通过ClassName.__doc__查看。

class_suite 类体由类的成员、方法、数据属性组成。

#coding=utf-8
class Employee(object):
      #所有员工基类
      empCount = 0
      def __init__(self, name, salary) :     #类的构造函数
            self.name = name
            self.salary = salary
            Employee.empCount += 1
      def displayCount(self) :            #实例方法
            print ("total employee ",Employee.empCount)
      def displayEmployee(self) :
            print ("name :",self.name , ", salary :", self.salary)

emp1 = Employee("wulaoshi",100) 
emp2 = Employee("lilaoshi",500) 
emp1.displayEmployee() 
emp2.displayEmployee() 
print ("Total Employee %d" % Employee.empCount)

 说明:

  1. empCount变量是一个类变量(也叫静态变量),它的值将在这个类的所有实例之间共享,可以在内部类或外部类使用
  2. 类通过方法(在class语句内由def语句编写的)为实例提供行为。因为这种嵌套的def会在类中对变量名进行赋值,实际效果就是把属性添加到类对象之中,从而可以由所有实例和子类继承。def语句块出现在类内部时,通常就叫方法,而且会自动接收第一个特殊参数(通常为self),这个参数代表了被处理的实例。
  3. 类中第一个方法__init__()是类的构造方法,创建类的实例会调用此方法,如果没有显式的定义此方法时,默认会定义一个空的构造方法
  4. 第一个参数为self的方法代表的是实例方法,self表示的就是实例本身
  5. 所有类最底层的基类都是object,不写的话默认继承的就是object

1.4 创建实例对象

要创建一个类的实例,可以使用类的名称,并通过__init__()方法来接受参数。__init__()方法是python自动调用的

class Person: #父类
	Count = 0
	def __init__(self,name):
		self.name = name
		print(id(self.name))
	
	def get_name(self):#self:传递的参数是实例的内存地址,告诉这个方法,使用哪个实例的数据
		"""获取名字"""
		return self.name
	
	def set_name(self,value):
		self.name = value

class Teacher(Person): #子类
	"""此类主要用于管理老师的数据"""
	def __init__(self,name,salary):
		Person.__init__(self,name)
		print(id(self.name))
		# self.name = name
		self.salary = salary
		Teacher.Count += 1
		self.Count = 100
	
	def get_name(self):#override:重写父类方法
		return "光荣之路" + self.name

t = Teacher("吴老师",1000)
print(t.get_name())
s = Teacher("李老师",2000)
print(s.get_name())
print(Teacher.__doc__)
print(Person.get_name.__doc__)#打印:获取名字
print(t.get_name.__doc__) #打印:None
print(Teacher.Count) # 打印:2
print(t.Count) #打印:100

1.5 访问类的实例方法或属性

访问对象的属性及方法,需要使用(.)来访问。如果访问类变量,既可以直接使用类名称访问,也可以用类的实例访问。
注意:在实例化类时,不需要再显示写self参数了,会默认带入,表示指向实例本身。

class P:
	def __init__(self,a):
	        self.a = a
	def print_str(self):
	        print("P")
	def func(self):   #访问类内的实例方法
	        self.print_str()

p = P(2)#实例化
print(p.a)#访问实例属性
p.func()#访问实例方法

1.6 类和对象在内存中是如何保存

类及类中的方法和类属性在内存中只有一份,而每个对象在内存中都有一份。

根据类创建对象时,对象中除了保存 name 和 age 的值之外,还会保存一个类对象指针,该值指向当前对象的类。
当通过obj1执行【方法一】时过程如下:根据当前对象中的类对象指针找到类中的方法将对象obj1当作参数传给方法的第一个参数 self 

1.7 封装和调用

1.7.1 封装

封装,顾名思义就是将内容封装到某个地方,以后再去调用被封装在某处的内容。

class Foo:
    def __init__(self, name, age):
        self.name=name
        self.age=age
#根据类Foo创建对象
#自动执行Foo类的__init__方法    
obj1=Foo('wu',18) #将wu和18分别封装到obj1/self的name和age属性中
print (obj1.name)# 直接调用obj1对象的name属性
print (obj1.age)# 直接调用obj1对象的age属性
obj2=Foo('alex',73)
print (obj2.name)# 直接调用obj2对象的name属性
print (obj2.age) # 直接调用obj2对象的age属性

说明:

  • self 是一个形式参数,当执行 obj1 = Foo('wui', 18 ) 时,self 等于 obj1;
  • 当执行 obj2 = Foo('alex',73) 时,self 等于 obj2
  • 内容其实被封装到了对象 obj1 和 obj2 中,每个对象中都有name和age属性
class Animal(object): 
	def __init__(self, name): #构造方法一个对象创建后会立即调用此方法 
		self.Name = name 
		print(self.Name)
	
	def accessibleMethod(self):
	#绑定方法对外公开
		print ("I have a self! current name is:")
		print (self.Name)
		print ("the secret message is:")
		self.__inaccessible()
	
	def __inaccessible(self): 
	#私有方法对外不公开以双下划线开头
		print ("U cannot see me...")
	
	@staticmethod 
	def staticMethod(): 
		# self.accessibleMethod() #在静态方法中无法 #直接调用实例方法直接抛出NameError异常 
		print ("this is a static method")
		
	def setName(self,name):
	#访问器函数
		print("setName:")
		self.Name=name
	
	def getName(self): 
	#访问器函数
		print("getName:")
		return self.Name 

a = Animal("learns")#打印:learns
print(a.getName)#打印:<bound method Animal.getName of <__main__.Animal object at 0x000001AF06927A60>>
a.setName("sr")#打印:setName:
print("new name:",a.getName())
"""打印
getName: 
new name:learns
"""
a.staticMethod()#打印:this is a static method
a.accessibleMethod()
"""
打印
I have a self! current name is:
sr
the secret message is:
U cannot see me...
"""

1.7.2 调用

调用被封装的内容时,有两种情况:

  • 通过对象直接调用
  • 通过 self 间接调用

1、通过对象直接调用被封装的内容

上述代码展示了对象 obj1 和 obj2 在内存中保存的方式,根据保存格式可以如此调用被封装的内容:对象.属性名

2、通过self间接调用被封装的内容

执行类中的方法时,需要通过self间接调用被封装的内容

class Foo:
	def __init__(self, name, age):
		self.name = name
		self.age = age
	
	def detail(self):
		print(self.name) #方法2的调用
		print(self.age) #方法2的调用

obj1 = Foo('wulaoshi', 18)
obj1.detail() # Python默认会将obj1传给self参数,即:
#obj1.detail(obj1) 
#所以,此时方法内部的 self = obj1,即:self.name 是 #wulaoshi ;self.age 是 18

obj2 = Foo('alex', 73)
obj2.detail() # Python默认会将obj2传给self参数,即: obj2.detail(obj2)
#所以,此时方法内部的 self = obj2,即:self.name 是 #alex ; self.age是 78

1.8 类成员

类的成员可以分为三大类:字段、方法和属性

1.8.1 字段

字段包括:普通字段和静态字段,他们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同

  • 普通字段,又叫实例变量:属于对象,在每个对象中都要保存一份,类中由普通方法调用,类外由对象调用,普通方法调用方式:self.普通字段;对象调用方式:对象名.普通字段
  • 静态字段,又叫类变量、静态成员变量:属于类,在内存中只保存一份,类变量在所有实例化的对象中是公用(共享)的。类中所有方法都可调用,类外类和对象都可调用,类中普通方法调用方式:类名.静态字段;类中类方法调用方式:类名.静态字段或cls.静态字段;类外对象调用方式:对象名.静态字段

类变量紧接在类名后面定义,相当于java和c++的static变量。

实例变量在__init__()函数里定义,相当于java和c++的普通变量。

class P:
	def __init__(self,name):
		self.name=name
	
	def print_info(self): #普通方法(实例方法)
		print("info method is inworked!")
		self.age=23
		print(self.age)


a=P("wulaoshi")
a.print_info()
print(a.age)
b=P("Amy")
print(b.name)
print(b.age)#报AttributeError: 'P' object has no attribute 'age'

实例变量和类变量重名时,使用实例访问这个变量的时候:优先访问实例变量,del删除之后访问的就是类变量。

>>> class Employee(object):
...     #所有员工基类
...     empCount = 0
...
...     def __init__(self,name,salary):
...             #类的构造函数
...             self.name = name
...             self.salary = salary
...             Employee.empCount += 1
...
...
>>> a=Employee("AAA",1000)
>>> a.name
'AAA'
>>> a.empCount
1
>>> a.empCount=3
>>> a.empCount
3
>>> Employee.empCount
1
>>> del a.empCount
>>> a.empCount
1

class f(object):
	count = 12#类变量


t = f() 
print("t.count内存地址 =", id(t.count), "t.count =", t.count)# t.count = 12
print("f.count内存地址 =", id(f.count), "f.count =", f.count)# f.count = 12,id(f.count)与id(t.count)相同
t.count = 10
print("通过实例对象修改count的值后")
print("t.count内存地址 =", id(t.count), "t.count =", t.count)
print("f.count内存地址 =", id(f.count), "f.count =", f.count)# f.count = 12,id(f.count)与id(t.count)不同

 局部变量

在类的实例方法中定义的变量,叫局部变量,其作用域仅限于其所在的函数内部,执行到该函数,局部变量生成,函数退出后,局部变量被消亡。

class Person(object):
	id = 12 #类静态成员在这儿定义,注意缩进 
	def __init__(self,name): 
		self.name = name #类的实例变量 
		self.inName = 'ads' #类的实例变量 
		
	def getName(self) : 
		name = "lucy" #局部变量 
		return self.name, name 


p = Person('linda') 
print (p.name) #访问对象的实例变量 #通过函数访问实例变量和局部变量 
res = p.getName() 
print (res)
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值