内容目录

类、对象

# 面向对象和面向过程的区别
# 面向过程(手洗):需要实现一个功能的时候,着重的是过程,分析出一个个步骤,
# 并把一个个步骤用一个个函数实现,再依次去调用一个个函数即可(每一个步骤都需要自己亲力亲为)
# 面向对象(机洗):需要实现一个功能的时候,着重的是谁去帮我做这件事情(偷懒,找别人帮我做)

# 类和对象
# 类就是一系列具有相同属性和行为的事物的统称,不是真实存在的事物
# 对象是类的具体实现,是类创建出来的真实存在的事物,面向对象思想的核心。
# 在开发中,先有类,再有对象

# 类的三要素
# 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
最后修改日期: 2025年6月2日

留言

撰写回覆或留言

发布留言必须填写的电子邮件地址不会公开。