面向对象
- 与面向过程对比:
- 面向过程:数学逻辑的映射,学会做个好员工
- 面向对象:生活逻辑的映射,学会做个好领导
- 官方定义:
- 类:具有相同特征(属性和行为)的事物的抽象
- 对象:某个类的具象
- 编程语言:
- 类:是一种自定义的数据类型
- 对象:某个类类型的变量
面向对象语法
-
类的定义:
class 类名:
内容 -
语法说明:
- 定义类需要使用关键字class
- 类名:原则上只要符合标识符命名规范即可,但是通常我们都使用大驼峰(每个单词首字母大写)风格命名
- 如:UserName
- 类名后面的’:'不要忘记
- 类的内容要进行整体缩进
- 行为:通过方法体现,在类中定义相关的函数即可(第一个参数通常是self)
- 属性:通过变量体现,在需要时动态添加,很多时候定义类时没有体现
- 成员访问:
- 属性:对象.属性名
- 方法:对象.方法名()
-
示例:
# 定义类
class Person:
# 行为通过方法体现# 吃饭 def eat(self): print('红烧鸡腿我喜欢吃') # 睡觉 def sleep(self): print('睡觉也是一种生活态度') # 定义对象 liang = Person() # 调用方法 liang.eat() liang.sleep() # 属性时动态添加的 liang.name = '某某某' # 获取属性 print(liang.name)
-
self使用
class Person:
def run(self):
# self表示当前对象:谁调用该方法就表示谁
print(’{}每天以2m/s的速度慢跑5km’.format(self.name))def introduce(self): # 不但可以访问成员属性 print('我叫{}'.format(self.name)) # 还可以调用成员方法 self.run() fei = Person() fei.name = '路人甲' fei.run() fei.introduce() long = Person() long.name = '路人乙' long.introduce()
- 说明:
1.类中的每个成员方法都有一个self参数,调用的时候不需要传递该参数
2.该参数的名字可以不是self,只不过通常我们都使用这个
3.self表示当前对象,对调用该方法self就代表谁,哪个对象调用就表示哪个对象
4.通过self可以访问成员属性,也可以调用成员方法
- 说明:
-
__str__方法
class Person:
# 使用print方法打印对象,默认打印 类名 + 地址
# 若想打印特定内容,重写该方法即可,要求返回一个字符串
def str(self):
return ‘我叫{},今年{}’.format(self.name, self.age)james = Person() james.name = '路人丙' james.age = 33 print(james)
-
构造方法:创建对象后,初始化属性时,系统会自动调用该方法
class Cat:
def str(self):
return ‘name:{},age:{},color:{}’.format(self.name, self.age, self.color)# 构造方法:创建对象后,初始化系统就会自动调用该方法 def __init__(self, name, age, color): print('__init__') self.name = name self.age = age self.color = color # 这种形式比较繁琐 # tom = Cat() # tom.name = 'Tom' # tom.age = 3 # tom.color = '蓝色' # 这种比较简洁 tom = Cat('Tom', 3, '蓝色') print(tom)
-
析构方法:当对象释放时系统会自动调用,通常用于释放资源
class Pig:
# 析构方法:当对象释放时,系统会自动调用
# 若手动使用del删除,则会立即调用该方法
# 该方法一般做资源释放处理:数据库连接断开,文件关闭
def del(self):
print(‘大师兄,我不行了’)bajie = Pig() del bajie print('八戒,一路走好!')
-
示例:小明手里有两张牌,左右♥K,右手♠A,小明交换两手的牌后,手里分别是什么?
-
思路:
- 先找到对象:左手、右手、♥K、♠A、小明
- 根据对象抽象出对应的类:人、手、牌
- 根据需要写出相应的逻辑,很可能反过来完善类的设计
- 按照题目要求创建相关对象,调用相关方法,实现相关功能
-
代码:
对象:小明、左手、右手、♥K、♠A
类:人、手、牌
设计相应的类
扑克牌
class Poker:
def init(self, color, number):
self.color = color
self.number = numberdef __str__(self): return '{}{}'.format(self.color, self.number)
创建两张牌
p1 = Poker(‘♥’, ‘K’)
p2 = Poker(‘♠’, ‘A’)手的类
class Hand:
def init(self, poker):
self.poker = pokerdef hold_poker(self, poker): self.poker = poker
创建左右两只手
left_hand = Hand(p1)
right_hand = Hand(p2)人的类
class Person:
def init(self, name, left_hand, right_hand):
self.name = name
self.left_hand = left_hand
self.right_hand = right_hand# 展示手里的牌 def show(self): print('{}张开手'.format(self.name), end=' ') print('左手:{}'.format(self.left_hand.poker), end=',') print('右手:{}'.format(self.right_hand.poker)) # 交换两手的牌 def swap(self): self.left_hand.poker, self.right_hand.poker = self.right_hand.poker, self.left_hand.poker print('{}交换两手的牌'.format(self.name))
创建小明对象
xiaoming = Person(‘小明’, left_hand, right_hand)
展示手里的牌
xiaoming.show()
交换两手的牌
xiaoming.swap()
#再次展示
xiaoming.show()
-
常用内置函数
-
内置函数:在类的内部,特定时机自动触发的函数。
-
示例:setattr、getattr、delattr
class Person:
def init(self, name):
self.name = namedef __str__(self): return '姓名:{}'.format(self.name) def __del__(self): print('对象即将销毁') # 当获取不存在的属性时,会自动触发该方法 def __getattr__(self, item): if item == 'age': return 18 else: return '你猜' # 当设置不存在的属性时,会自动触发该方法 def __setattr__(self, key, value): print(key, value) self.__dict__[key] = value # 销毁对象成员属性时,会自动触发该方法 def __delattr__(self, item): print(item, '即将销毁') xiaoming = Person('小明') xiaoming.age = 20 print(xiaoming.age) # 存放对象的所有属性 # print(xiaoming.__dict__) # print(xiaoming) del xiaoming.age
-
将对象当做字典操作,特定时机会自动触发的方法
class Person:
# 将对象当做字典操作,设置键值对时会触发该方法
def setitem(self, key, value):
# print(key, value)
self.dict[key] = value# 将对象当做字典操作,根据键获取值时会触发该方法 def __getitem__(self, item): # print(item) return self.__dict__.get(item) # 将对象当做字典操作,删除指定的键值对时自动触发 def __delitem__(self, key): del self.__dict__[key] xiaoming = Person() xiaoming['name'] = '小明' print(xiaoming.dict) print(xiaoming['name']) del xiaoming['name']
-
将对象当做函数调用时,会自动触发下面方法
class Person:
# 将对象当做函数调用时,会自动触发该方法
def call(self, *args, **kwargs):
# print(‘call’)
return sum(args)xiaoming = Person() # 这样操作,需要提供call方法 ret = xiaoming(1, 2, 3, name='小明') print(ret)
-
函数判断
class A:
def call(self, *args, **kwargs):
passdef test(): pass a = A() # 判断是否可调用 print(callable(test)) print(callable(a)) # 判断是否拥有'__call__'属性 print(hasattr(test, '__call__')) print(hasattr(a, '__call__')) # 判断是否是函数 from inspect import isfunction print(isfunction(test)) print(isfunction(a))
面向对象三大特点
- 封装:既可对数据结构进行封装,又可对处理数据的方法进行封装
- 继承:强调的父子类的关系
- 多态:不同对象调用相同的方法,会有不同的响应
类的继承
-
相关概念
- 继承:父类的属性和方法,子类直接拥有,称为继承
- 派生:子类在父类的基础上衍生出新的特征(属性和行为)
- 总结:其实他们是一回事,只是描述问题的侧重点不同(继承强调相同点,派生强调不同点)
-
继承语法
# class Animal(object):
# 当没有指定父类时,默认继承object
class Animal:
def init(self, name):
self.name = namedef eat(self): print('小动物喜欢一天到晚吃个不停') # 继承自Animal class Dog(Animal): pass d = Dog('旺财') # 可以拥有父类的方法 d.eat() # 也可以拥有父类的属性 print(d.name)
-
派生示例
class Animal:
def run(self):
print(‘小动物喜欢成天跑个不停’)class Cat(Animal): def eat(self): print('猫喜欢吃老鼠') tom = Cat() tom.run() # 多出来的行为 tom.eat() # 多出来的属性 tom.color = '蓝色' print(tom.color)
-
重写方法
-
若父类的方法完全不合适,可以进行覆盖重写
-
若父类的方法不够完善,可以添枝加叶进行完善
-
示例:
class Animal:
def eat(self):
print(‘小动物一天到晚的出个不停’)def run(self): print('小动物一天到晚的四处连跑')
class Cat(Animal):
# 父类的方法完全不合适,覆盖重写
def run(self):
print(‘俺走的时猫步’)# 父类的方法部分合适,需要添加内容进行完善 def eat(self): # 保留父类的内容,不建议使用此方式 # Animal.eat(self) # super(Cat, self).eat() # 类名及self可以不传 super().eat() print('不过俺喜欢吃鱼')
jiafei = Cat()
jiafei.run()
jiafei.eat()
-
-
多继承:一个子类可以拥有多个父类
class A:
def eat(self):
print(‘eat func in class A’)class B: def eat(self): print('eat func in class B') class C(A, B): def eat(self): # 这种方案是默认的继承顺序进行选择的父类方法 # super().eat() # 人为指定调用某个父类的方法 B.eat(self) c = C() c.eat()
访问权限
-
权限
- 公有的:类中的普通的属性和方法,默认都是公有的;可以在类内、类外、子类中使用
- 私有的:定义时在前面添加两个’_’,就变成了私有的;只能在类内使用,不能在类外及子类中使用
-
示例:
class Person:
def init(self, name):
self.name = name
self.__age = 20def eat(self): print('民以食为天') def __test(self): print('__test') xiaoming = Person('小明') print(xiaoming.name) xiaoming.eat() #不能在类外使用 #print(xiaoming.__age) xiaoming._Person__test() print(xiaoming.dict) #尽管可以这样访问私有属性,但是强烈建议不要这样使用 #print(xiaoming._Person__age) class Man(Person): def introduce(self): # 不能在子类中使用 # print(self.__age) print('我叫{}'.format(self.name)) self.eat() m = Man('亮亮') m.introduce()
类属性
-
说明:定义类时,写在方法外的属性,通常会写在类的开头,这样的属性称为类属性
-
示例:
class Person:
# 类属性,通过类名访问,属于整个类,而不是某个对象
# nation = ‘中国’# 限制可以使用的属性,提高访问的效率 # 也可以提高访问速度,减少内存使用 __slots__ = ('name', 'age', 'nation') def __init__(self, name): self.name = name self.nation = 'china' p1 = Person('小明') p2 = Person('小红') print(p1.name, p2.name) print(Person.nation) #p1.nation = 'china' print(p1.nation) print(p2.nation) #print(Person.nation) p1.age = 20 #p1.height = 180 #特殊的类属性 #表示类名的字符串 print(Person.name) #表示父类构成的元组 print(Person.bases) #存储类的相关信息 print(Person.dict) #限制可以使用的属性 print(Person.slots)
类方法
-
说明:
- 定义时使用classmethod装饰器装饰的方法
- 使用时通过类名调用
-
作用:
- 可以创建对象或者简洁的创建对象
- 对外提供简单易用的接口
-
示例1:创建对象
class Person: def eat(self): print('我喜欢吃麻辣烫,不要麻椒和辣椒') @classmethod def test(cls): print(cls) print('类方法') # 创建对象,或者简洁的创建对象 @classmethod def create(cls): p = cls() p.age = 1 return p p1 = Person() p1.eat() # 通过类名调用 Person.test() # 创建或简洁的创建对象 p2 = Person.create() print(type(p2))
-
示例2:提供接口
class Number:
def init(self, num1, num2):
self.num1 = num1
self.num2 = num2def add(self): return self.num1 + self.num2 def sub(self): return self.num1 - self.num2 def mul(self): return self.num1 * self.num2 def div(self): if self.num2 == 0: return None return self.num1 / self.num2 # 对外提供简单易用的接口 @classmethod def pingfanghe(cls, num1, num2): n1 = cls(num1, num1) n12 = n1.mul() n2 = cls(num2, num2) n22 = n2.mul() n3 = cls(n12, n22) return n3.add() he = Number.pingfanghe(3, 4) print(he)
静态方法
-
说明:
- 使用staticmethod装饰器装饰的方法(方法没有cls参数)
- 通过类名进行调用
-
示例:
class Person:
@staticmethod
def test():
print(‘static method test’)# 创建对象 @staticmethod def create(): p = Person() return p #Person.test() #p = Person.create() #print(p)
、 class Animal:
def run(self):
pass
class Dog(Animal):
def run(self):
print(‘狗通常走S型’)
class Cat(Animal):
def run(self):
print(‘猫平时走猫步,偶尔突然加速’)
def test(obj):
obj.run()
d = Dog()
c = Cat()
test(d)
test©
属性函数
-
说明:将成员方法当做属性一样进行访问
-
作用:保护特定属性,或者对特定属性进行处理
-
示例:
class User: def __init__(self, username, password): self.username = username self.__password = password # 该方法可以像成员属性一样访问 @property def password(self): print('有人想查看密码') return '想偷看密码,没门' # return self.__password # 在设置密码时,会自动调用 @password.setter def password(self, password): print('@password.setter', password) self.__password = '加密' + password + '加密' u = User('xiaoming', '111111') # print(u.password()) print(u.password) # 设置密码,会自动调用setter方法 u.password = 'abcde'
面向对象及异常处理
内置函数
-
总结:init、del、str、attr系列、item系列、call
-
str__与__repr
- 使用print/str方法时会自动触发__str__方法,当__str__不存在,尝试__repr__
- 使用repr方法时会自动触发__repr__方法
- repr方法通常用于返回对象的字符串表示形式
- 这两个方法都只能返回字符串
- eval方法可以将一个字符串当做有效的代码执行
-
算术运算符重载
-
示例:
class Number:
def init(self, num):
self.num = num# 对象出现在'+'左边时会自动触发 def __add__(self, other): print('__add__') return self.num + other # 对象出现在'+'右边是会自动触发 def __radd__(self, other): print('__radd__') return self.num + other # +=运算时会自动触发,没有时会触发 __add__ def __iadd__(self, other): print('__iadd__') return Number(self.num + other)
n = Number(100)
ret = n + 200
ret = 200 + n
print(ret)
n += 200 # n = n + 200
print(n) -
自己测试
加法:add、radd、iadd
减法:sub、rsub、isub
乘法:mul、rmul、imul
除法:truediv、rtruediv、itruediv
求余:__mod、rmod、imod
-
-
关系运算符重载
>: gt
=: ge
<: lt
<=: le
==: eq
!=: ne -
示例
class Number:
def init(self, num):
self.num = numdef __gt__(self, other): print('__gt__') return self.num > 200 def __lt__(self, other): print('__lt__') return self.num < other def __eq__(self, other): print('__eq__') return self.num == other # 当没有此方法时,使用!=也会触发__eq__方法 def __ne__(self, other): print('__ne__') return self.num != other n = Number(100) # print(n > 200) # print(200 > n) # print(200 == n) print(200 != n)
深浅拷贝
-
引用计数
- python中的所有变量都是对象,对象的管理采用的时引用计数的方式
- 当多一个变量指向对象计数值加1,当少一个变指向对象计数值减1,减到0是,释放对象(del)
-
函数传参
- 对于不可变的变量来说,函数中不可能改传递过来的变量
- 对于可变的容器对象及自定义对象,作为函数参数传递时,传递的是引用,可以修改该对象
-
深浅拷贝
class Person:
def del(self):
print(‘对象释放’)p1 = Person() p2 = p1 print(id(p1)) print(id(p2)) del p1 del p2 print('OVER') def test(m): # m += 1 m[0] = 300 # n = 100 n = [100, 200] test(n) print(n) import copy lt = [1, 2, [3, 4]] # 浅拷贝,只拷贝对象本身,不拷贝对象中的元素 # lt2 = lt.copy() # 浅拷贝 lt2 = copy.copy(lt) # 深拷贝:不但拷贝对象本身,还拷贝对象中的元素 lt2 = copy.deepcopy(lt) lt[0] = 100 lt2 = 300 print(id(lt)) print(id(lt2)) print(lt) print(lt2)
数据持久化(pickle)
-
说明:数据持久化存储方案,普通文件、序列化、数据库
-
示例:
import pickleclass Person: def __init__(self, name, age): self.name = name self.age = age def __str__(self): return 'name:{},age:{}'.format(self.name, self.age) xiaoming = Person('xiaoming', 20) # 转换为bytes类型 # s = pickle.dumps(xiaoming) # print(s) # 从字节流中提取对象 # xm = pickle.loads(s) # print(xm) # 保存到文件中 # fp = open('data.txt', 'wb') # pickle.dump(xiaoming, fp) # 从文件中获取对象 fp = open('data.txt', 'rb') xm = pickle.load(fp) print(xm)
异常处理
-
相关概念
- 错误:程序运行之前的语法问题,如:关键字、缩进、括号不成对等
- 异常:在程序运行过程中出现的问题,如:未定义变量、除数为0、属性不存在等
-
异常处理
- 说明:异常处理可以理解为特殊的流程控制语句,可以提高代码的健壮性。
-
异常语法:
try:
print(‘正常代码’)
# print(a)
3/0
except Exception as e:
# Exception 是所有异常的基类,此处可以捕获所有的异常
print(‘出现异常’)
print(e)print('其他内容')
-
多个异常
# 分类捕获异常
‘’’
try:
# print(a)
# 3/0
d = {}
print(d[‘name’])
except NameError as e:
print(‘NameError:’, e)
except ZeroDivisionError as e:
print(‘ZeroDivisionError:’, e)
except Exception as e:
print(‘OtherError:’, e)
‘’’try: # print(a) # 3/0 fp = open('123.txt') except (NameError, ZeroDivisionError) as e: # 将某些异常进行统一处理,写在一个元组中即可 print(e) except: print('其他异常')
-
完整结构(else-finally)
try:
print(‘正常代码’)
print(a)
except:
# 出现异常时执行
print(‘出现异常’)
else:
# 正常结束(没有异常)时会执行
print(‘正常结束’)
finally:
# 无论有无异常,都会执行
print(‘最后执行’)
else:正常结束时执行else中的代码
finally:无论有无异常,最后都执行 -
抛出异常:raise
try:
print(‘正常代码’)
# 根据业务逻辑的需要,手动抛出异常
raise Exception(‘手动抛出的异常’)
except Exception as e:
print(‘异常:’, e)print('OVER')
-
异常嵌套(try-except结构中再次使用try-except结构)
print(‘我要去上班,什么事也阻止不了我上班的脚步’)
try:
print(‘我准备骑电动车’)
raise Exception(‘昨天晚上不知道哪个缺德的家伙把我充电器拔了’)
print(‘骑车提前到达公司’)
except Exception as e:
print(e)
try:
print(‘我准备做公交车’)
raise Exception(‘等了20分钟一直没有公交车,果断放弃’)
print(‘坐公交车准时到达公司’)
except Exception as e:
print(e)
print(‘我准备打车’)
print(‘打车还是快,一会就到达公司’)print('热情满满的开始一天的工作')
-
自定义异常类(需要继承自官方的异常基类Exception)
# 自定义异常类
class MyException(Exception):
def init(self, msg):
self.msg = msgdef __str__(self): return self.msg # 特定异常标准处理方案 def deal(self): print('处理特定的自定义异常') try: print('正常执行') # 手动抛出自定义异常 raise MyException('出现了自定义异常') except MyException as e: print(e) # 调用方法,处理异常 e.deal()
-
特殊场景