内容目录
类、对象
# 面向对象和面向过程的区别
# 面向过程(手洗):需要实现一个功能的时候,着重的是过程,分析出一个个步骤,
# 并把一个个步骤用一个个函数实现,再依次去调用一个个函数即可(每一个步骤都需要自己亲力亲为)
# 面向对象(机洗):需要实现一个功能的时候,着重的是谁去帮我做这件事情(偷懒,找别人帮我做)
# 类和对象
# 类就是一系列具有相同属性和行为的事物的统称,不是真实存在的事物
# 对象是类的具体实现,是类创建出来的真实存在的事物,面向对象思想的核心。
# 在开发中,先有类,再有对象
# 类的三要素
# 1.类名
# 2.属性:对象的特征描述,用来说明是什么样子的
# 3.方法:对象具有的功能(行为),用来说明能够做什么
# 定义类
# 基本格式
# class 类名:必须符合标识符规定,同时遵循大驼峰命名法,见名知义
# 代码块
# 洗衣机类
class Washer:
height = 800 # 类属性:就是类所拥有的属性
# 查看类属性:类名.属性名
print(Washer.height)
# 新增类属性:类名.属性名 = 值
Washer.width = 450
print(Washer.width)
# 创建对象
# 创建对象的过程也叫做实例化对象
# 实例化对象的基本格式:对象名 = 类名()
# 实例化一个洗衣机对象
wa = Washer()
print(wa) # 显示的是对象在内存中的地址
# 第二次实例化
wa2 = Washer()
print(wa2)
# 内存地址不一样,说明是不同的对象,可以实例化多个对象
800
450
<__main__.Washer object at 0x000001DF02823A00>
<__main__.Washer object at 0x000001DF028239D0>
# 实例方法和实例属性
# 实例方法
# 由对象调用,至少有一个self参数,执行实例化方法的时候,自动将调用该方法的对象赋值给self
class Washer:
height = 800 # 类属性
def wash(self): # self参数是类中实例方法必须具备的
print("我会洗衣服")
print("方法中的self:",self) # self表示当前调用该方法的对象
# 实例化对象
wa = Washer()
print("wa:",wa)
# 对象调用类中的方法
wa.wash()
wa2 = Washer()
print("wa2:",wa2)
wa2.wash()
# self代表对象本身,当对象调用实例方法时,python会自动将对象本身的引用作为参数,传递到实例方法的第一个参数self里面
wa: <__main__.Washer object at 0x000001388414BFD0>
我会洗衣服
方法中的self: <__main__.Washer object at 0x000001388414BFD0>
wa2: <__main__.Washer object at 0x000001388414BEE0>
我会洗衣服
方法中的self: <__main__.Washer object at 0x000001388414BEE0>
# 实例属性
# 格式:self.属性名
class Person:
name = "bingbing"
def introduce(self):
print("我是实例方法")
# self.age是实例属性
print(f"{Person.name}的年龄:{self.age}")
pe = Person()
pe.age = 18
pe.sex = '女' # 新增实例属性,是给pe对象新增的实例属性,其他对象依然是没有这个属性的
print(pe.sex) # 根据对象名访问实例属性
pe.introduce()
# 实例属性和类属性的区别
# 类属性属于类,是公共的,大家都能访问到,实例属性属于对象的,是私有的
# 只实例属性只能由对象名访问,不能由类名访问
# 访问类属性,类可以访问到,实例对象也可以访问到
print(Person.name)
print(pe.name)
# 每实例化一次就需要添加一次,效率不高
女
我是实例方法
bingbing的年龄:18
bingbing
bingbing
构造函数
# 构造函数__init__()
# 作用:通常用来做属性初始化或者赋值操作
# 注意:在类实例化对象的时候,会被自动调用
class Test:
def __init__(self): # 实例方法
print("这是__int__()函数")
# 实例化对象:对象名 = 类名()
te = Test()
class Person:
def __init__(self, name, age,height):
self.name = name # 实例属性
self.age = age
self.height = height
def play(self):
print(f'{self.name}在打王者荣耀')
def introduce(self):
print(f'{self.name}的年龄是{self.age},身高是{self.height}cm')
# 实例化对象
pe = Person('bingbing',18,183)
pe.play()
pe.introduce()
# 实例化第二个对象
pe2 = Person('ziyi',18,168)
pe2.play()
pe2.introduce()
这是__int__()函数
bingbing在打王者荣耀
bingbing的年龄是18,身高是183cm
ziyi在打王者荣耀
ziyi的年龄是18,身高是168cm
析构函数
# 析构函数 __del__()
# 删除对象的时候,解释器会默认调用 __del__()方法
class Person:
def __init__(self):
print("我是__init__()")
def __del__(self):
print("被销毁了")
p = Person()
del p # 删除p这个对象
# del p语句执行的时候,内存会立即被回收,会调用对象本身的__del__()方法
print("这是最后一行代码")
# 正常运行时,不会调用__del__(),对象执行结束之后,系统会自动调用__del__()
# __del__()主要是表示该程序或者函数已经全部执行结束
我是__init__()
被销毁了
这是最后一行代码
封装
# 封装
# 面向对象的三大特性:封装、继承、多态
# 封装:指的是隐藏对象中一些不希望被外部所访问到的属性或者方法
class Persen:
name = 'bingbing' # 类属性
pe = Persen()
print(pe.name)
Persen.name = 'ziyi'
print(Persen.name)
# 隐藏属性(私有权限),只允许在类的内部使用,无法通过对象访问
# 在属性名或者方法名前面加上两个下划线
class Humen:
name = 'James' # 类属性
__age = 28 # 隐藏属性
def introduce(self): # 实例方法
Humen.__age = 18
print(f"{Humen.name}的年龄是{Humen.__age}")
# 在实例方法中访问类属性和隐藏属性
pe = Humen()
print(pe.name)
# 第一种:
# 隐藏属性实际上是将名字修改为:_类名__属性名
print(pe._Humen__age)
pe._Humen__age = 18
print(pe._Humen__age)
# 第二种:在类的内部访问(推荐使用,正规手段)
pe.introduce()
bingbing
ziyi
James
28
18
James的年龄是18
# 私有属性/方法
# 普通属性/方法,如果是类中定义的,则类可以在任意地方使用
# 隐藏属性/方法(双下划线开头):如果定义在类中,无法在外部直接访问,子类不会继承,
# 要访问只能通过间接的方式,另一个py文件中通过from xxx import *导入的时候,也无法导入
# 这种命名一般是python中的魔术方法或属性,都是有特殊含义或者功能的,自己不要轻易定义
# 私有属性/方法(单下划线开头):如果定义在类中,外部可以使用,子类也可以继承
# 但是在另一个py文件中通过from xxx import *导入时,无法导入
# 一般是为了避免与python关键字冲突而采用的命名方法
class Person:
name = "湖人"
__age = 28 # 隐藏属性
_sex = "男" # 私有属性
pe = Person()
# print(pe.sex) # 报错
print(pe._sex)
# 使用对象._类名__属性名访问隐藏属性
print(pe._Person__age)
class Man:
def __play(self): # 隐藏方法
print("玩手机")
def funa(self): # 平平无奇的实例方法
print("平平无奇的实例方法")
Man.__play(self) # 在实例方法中调用私有方法(不推荐)
self.__play() # 推荐使用,更简便
ma = Man()
ma.funa()
# ma._Man__play()
男
28
平平无奇的实例方法
玩手机
玩手机
# 私有方法
class Girl:
def _buy(self): # 私有方法
print("整天买买买")
girl = Girl()
girl._buy()
整天买买买
继承
# 就是让类和类之间转变为父子关系,子类默认继承父类的属性和方法
# class 类名(父类名):
# 代码块
# 单继承
class Person:
def eat(self):
print("我会吃饭")
def sing(self):
print("我是唱歌小能手")
class Girl(Person): # Person类的子类
pass # 占位符,代码里面类下面不写任何东西,会自动跳过,不会报错
class Boy(Person):
pass
girl = Girl()
girl.eat()
girl.sing()
boy = Boy()
boy.eat()
boy.sing()
# 总结:子类可以继承父类的属性和方法,就算子类自己没有,也可以使用父类的。
我会吃饭
我是唱歌小能手
我会吃饭
我是唱歌小能手
# 继承的传递(多重继承)
# A/B/C C(子类)继承于B(父类),B类(子类)继承A类(父类),C类具有A/B类的属性和方法
# 子类拥有父类的父类的属性和方法
class Father: # 父类
def eat(self):
print("吃饭")
def sleep(self):
print("睡觉")
class Son(Father): # Father类的子类
pass
class GrandSon(Son): # Son的子类
pass
son = Son()
son.eat()
son.sleep()
grandson = GrandSon()
grandson.eat()
grandson.sleep()
# 继承的传递性就是子类拥有父类以及父类的父类中的属性和方法
吃饭
睡觉
吃饭
睡觉
# 重写指在在子类中定义与父类相同名称的方法
# 覆盖父类方法
class Person: # 父类
def money(self):
print("一百万需要被继承")
def sleep(self):
print("睡觉了")
class Man(Person): # 子类
def money(self):
Person.money(self)
super().money()
super().sleep() # 可以调用父类中的方法
super(Man, self).money()
print("自己赚一千万")
man = Man()
man.money()
# 对父类方法进行扩展:继承父类的方法,子类也可以增加自己的功能
# 1.父类名.方法名(self)
# 2.super().方法名() 推荐使用
# super在python里面是一个特殊的类,super()是使用super类创建出来的对象,可以调用父类中的方法
# 3.super(子类名,self).方法名()
一百万需要被继承
自己赚一千万
新式类写法
# 第一种写法 class A:
# 经典类:不由任意内置类型派生出的类
class Animal:
def walk(self):
print('我会走路')
class Dog(Animal):
def bite(self): # Dog类是派生类
print('我会咬人')
# 第二种写法 class A():
# 第三种写法 class A(object): 新式类:继承了object类或者该类的子类都是新式类,推荐使用
# object --对象,python为所有对象提供的基类(顶级父类),提供了一些内置的属性和方法,可以使用dir()查看。
print(dir(object))
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
# python3中如果一个类没有继承任何类,则默认继承object类,因此python3都是新式类
多继承
# 子类可以拥有多个父类,并且具有所有父类的属性和方法
class Father(object): # 父类一
def money(self):
print('拥有100万财产需要被继承')
class Mother(object): # 父类二
def appearance(self):
print('绝世容颜需要被继承')
class Son(Father, Mother): # 子类
pass
son = Son()
son.money()
son.appearance()
拥有100万财产需要被继承
绝世容颜需要被继承
# 不同的父类存在同名的方法
# 开发时,需要尽量避免这种情况
# 有多个父类的属性和方法,如果多个父类具有同名方法的时候,调用就近原则
# 括号内哪一个离得最近,优先调用哪一个类得方法
# 方法的搜索顺序
# python中内置的属性,__mro__可以查看方法搜索顺序
class Father(object): # 父类一
def money(self):
print('拥有100万财产需要被继承')
class Mother(object): # 父类二
def money(self):
print('拥有200万财产需要被继承')
def appearance(self):
print('绝世容颜需要被继承')
class Son(Mother,Father): # 子类
pass
son = Son()
son.money()
son.appearance()
print(Son.__mro__)
# 搜索方法时,会先按照__mro__的输出结果,从左往右的顺序查找
# 如果在当前类中找到了方法,就直接执行,不再搜索
# 如果找到最后一个类,还没有找到这个方法,程序就会报错
拥有200万财产需要被继承
绝世容颜需要被继承
(<class '__main__.Son'>, <class '__main__.Mother'>, <class '__main__.Father'>, <class 'object'>)
# 多继承的弊端
# 容易引发冲突
# 会导致代码设计的复杂度增加
多态
# 指同一种行为具有不同的表现形式
# 多态的前提
# 继承
# 重写
print(10+10) # 算术运算符,可以实现整形之间的相加操作
print('10'+'10') # 字符串拼接,实现字符串之间的拼接操作
class Animal(object):
"""父类:动物类"""
def shout(self):
print('动物会叫')
class Cat(Animal):
"""子类一:猫类"""
def shout(self):
print('小猫喵喵喵')
class Dog(Animal):
"""子类二:狗类"""
def shout(self):
print('小狗汪汪汪')
cat = Cat()
cat.shout()
dog = Dog()
dog.shout()
小猫喵喵喵
小狗汪汪汪
# 多态性:一种调用方式,不同的执行结果
class Animal(object):
def eat(self):
print('我会干饭')
class Pig(Animal):
def eat(self):
print('猪吃猪饲料')
class Dog(Animal):
def eat(self):
print('狗吃狗粮')
# 多态性:定义一个统一的接口,一个接口多种实现
def test(obj):
obj.eat()
animal = Animal()
pig = Pig()
dog = Dog()
test(animal)
test(pig)
test(dog)
# test函数传入不同的对象,执行不同对象的eat方法
我会干饭
猪吃猪饲料
狗吃狗粮
静态方法
# 使用@staticmethon来进行修饰,静态方法没有self,cls参数的限制
# 静态方法与类无关,可以被转换成函数使用
class Person(object):
@staticmethod # 静态方法
def study(name):
print(f'{name}会学习')
# 静态方法既可以使用对象访问,也可以使用类访问
Person.study('bingbing')
pe = Person()
pe.study('bingbing') # 调用方法时传参数
bingbing会学习
bingbing会学习
# 取消不必要的参数传递,有利于减少不必要的内存占用和性能消耗
类方法
# 使用装饰器@classmethond来标识类方法,对于类方法,第一个参数必须是类对象,一般是以cls作为第一个参数
# 类方法内部可以访问类属性,或者调用其他的类方法
class Person(object):
name = 'bingbing' # 类属性
@classmethod
def sleep(cls):
print(cls) # cls代表类对象本身,类本质上就是一个对象
print('人类在睡觉')
print(cls.name)
print(Person)
Person.sleep()
# 当方法中需要使用到类对象(如访问私有类属性等),定义类方法
# 类方法一般是配合类属性使用
<class '__main__.Person'>
<class '__main__.Person'>
人类在睡觉
bingbing
# 1.实例方法:方法内部访问实例属性,方法内部可以通过类名.类属性名来访问类属性
# 2.静态方法@staticmethod:方法内部,不需要访问实例属性和类属性
# 如果要访问类属性,通过类名.类属性名访问,不能访问实例属性
# 3.类方法@classmethod:方法内部只需要访问类属性,可以通过cls.类属性名访问类属性,不能访问实例属性
class Person(object):
name = '小明' # 类属性:类所拥有的属性
def __init__(self):
self.age = 18 # 实例属性:对象私有的
def play(self): # 实例方法
# 在实例方法中访问类属性
print(f'{Person.name}在玩游戏')
print(self.age)
@staticmethod # 静态方法:类中的函数,形参没有限制
def introduce():
print(f'我是{Person.name}') # 静态方法能够访问到类属性,但是无意义
@classmethod # 类方法:针对类存在的方法
def introduce2(cls): # cls代表类对象本身
print(cls.name)
# print(self.age) # 报错
pe = Person()
pe.play()
print(pe.age)
pe.introduce()
pe.introduce2()
# 类属性是公共的,所有方法内部都能够访问到,实例属性是私有的,只有实例方法内部能够访问到
# 静态方法不需要访问类属性,因为静态方法和类、对象没有关联
小明在玩游戏
18
18
我是小明
小明
# __init__()和__new__()
# __init__():初始化对象
class Test(object):
def __init__(self):
print('这是__init__()')
print(self)
def __new__(cls, *args, **kwargs): # cls代表类本身
print('我是__new__()')
print(cls)
# 对父类方法进行扩展 super().方法名()
res = super().__new__(cls) #方法重写,res里面保存的是实例对象的引用
# __new__()是静态方法,形参里面有cls,实参就必须传cls
return res
# 注意:重写__new__()一定要return,否则python解释器得不到分配空间的对象引用,就不会调用__init__()
te = Test()
# __new__():object基类提供的内置的静态方法
# 作用:1.在内存中为对象分配空间 2.返回对象的引用
print(te)
我是__new__()
<class '__main__.Test'>
这是__init__()
<__main__.Test object at 0x00000134CC42BEB0>
<__main__.Test object at 0x00000134CC42BEB0>
# 执行步骤:
# 一个对象的实例化过程:首先执行__new()__,如果没有写__new__()
# 如果没有写__new()__,默认调用object里面的__new__(),返回一个实例对象
# 然后再去调用__init()__,对对象进行初始化
class Person(object):
def __new__(cls, *args, **kwargs):
print('这是new方法')
print(super().__new__(cls))
return super().__new__(cls)
def __init__(self, name):
self.name = name # 实例属性
print('名字是:',self.name)
pe = Person('bingbing')
print(pe)
pe2 = Person('susu')
print(pe2)
这是new方法
<__main__.Person object at 0x000001C12916FEB0>
名字是: bingbing
<__main__.Person object at 0x000001C12916FEB0>
这是new方法
<__main__.Person object at 0x000001C12916FBE0>
名字是: susu
<__main__.Person object at 0x000001C12916FBE0>
# 总结:__init__()和__new__()
# 1.__new__()是创建对象,__init__()是初始化对象
# 2.__new__()是返回对象引用,__init__()定义实例属性
# 3.__new__()是类级别的方法,__init__()是实例级别的方法
单例模式
# 可以理解成一个特殊的类,这个类只存在一个对象
# 优点:可以节省内存空间,减少了不必要的资源浪费
# 弊端:多线程访问的时候容易引发线程安全问题
# 方式
# 1.通过@classmethod
# 2.通过装饰器实现
# 3.通过重写__new__()实现
# 4.通过导入模块实现
class A(object):
pass
a1 = A()
print(a1)
a2 = A()
print(a2)
<__main__.A object at 0x0000021D64011060>
<__main__.A object at 0x0000021D64010FA0>
# 内存地址发生变化说明是不同的对象
# 实现单例模式 对象的内存地址都是一样的,只有一个对象
# 通过重写__new__()实现单例模式
# 设计流程
# 1.定义一个类属性,初始值为None,用来记录单例对象的应用
# 2.重写__new__()方法
# 3.进行判断,如果类属性是None,把__new__()返回的对象应用保存进去
# 4.返回类属性中记录的对象引用
class Singleton(object):
# 记录第一个被创建的对象的引用
obj = None
def __new__(cls, *args, **kwargs):
print('这是__new__()')
# 判断类属性是否为空
if cls.obj == None:
cls.obj = super().__new__(cls)
return cls.obj
def __init__(self):
print('我是__init__()')
s = Singleton()
print("s:",s)
s2 = Singleton()
print("s2:",s2)
# 单例模式:每一次实例化所创建的对象都是同一个,内存地址都一样
这是__new__()
我是__init__()
s: <__main__.Singleton object at 0x000002CC0025FBB0>
这是__new__()
我是__init__()
s2: <__main__.Singleton object at 0x000002CC0025FBB0>
# 通过导入模块实现单例模式
# 模块就是天然的单例模式
from pytest2 import te as te01
from pytest2 import te as te02
print(te01,id(te01))
print(te02,id(te02))
<pytest2.Test object at 0x000001B5AF2B1060> 1879839543392
<pytest2.Test object at 0x000001B5AF2B1060> 1879839543392
# 应用场景
# 1.回收站对象
# 2.音乐播放器,一个音乐播放软件负责音乐播放的对象只有一个
# 3.开发游戏软件 场景管理器
# 4.数据库配置、数据库连接池的设计
魔法方法
# 魔法方法&魔法属性
# __doc__:类的描述信息
class Person(object):
"""人类--类的描述信息""" # 只能使用多行注释,单行注释无效
pass
print(Person.__doc__)
人类--类的描述信息
# __module__:表示当前操作对象所在的模块
# __class__:表示当前操作对象所在的类
import pytest2
b = pytest2.B()
print(b)
b.funa()
print(b.__module__) # 输出模块
print(b.__class__) # 输出类
<pytest2.B object at 0x0000020E7F2113C0>
哈哈哈
pytest2
<class 'pytest2.B'>
# __str__():对象的描述信息
# 如果类定义了此方法,那么在打印对象时,默认输出该方法的返回值,也就是打印方法中return的数据
# 注意:__str__()必须返回一个字符串
class C:
def __str__(self):
return '这是str的返回值' # 必须要有返回值,并且一定是字符串类型
pass
c = C()
print(c)
这是str的返回值
# __del__():析构函数,在程序结束时会调用,或者在删除某个对象的时候也会被调用
# __call__():使一个实例对象成为一个可调用对象,就像函数那样可以调用
# 可调用对象:函数/内置函数/类都是可调用对象,凡是可以把一对()应用到某个对象身上都可以称之为可调用对象
# callable():判断一个对象是否是一个可调用函数
def func():
print('呵呵呵')
func()
print(callable(func))
name = 'bingbing'
print(callable(name))
呵呵呵
True
False
# __del__():析构函数,在程序结束时会调用,或者在删除某个对象的时候也会被调用
# __call__():使一个实例对象成为一个可调用对象,就像函数那样可以调用
# 可调用对象:函数/内置函数/类都是可调用对象,凡是可以把一对()应用到某个对象身上都可以称之为可调用对象
# callable():判断一个对象是否是一个可调用函数
class A:
def __call__(self, *args, **kwargs):
print('这是__call__()')
a = A()
a() # 调用一个可调用的实例对象,其实就是在调用它的__call__()方法
print(callable(a))
这是__call__()
True
留言