Python学习总结(对象篇)

dir 内置函数

__方法名__格式的方法是 Python 提供的 内置方法/属性

def print_dir():
    num = 10
    fun_names = dir(num)
    print(fun_names)

# 返回结果
['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__getstate__', '__gt__', '__hash__', '__index__', '__init__', '__init_subclass__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'as_integer_ratio', 'bit_count', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'is_integer', 'numerator', 'real', 'to_bytes']

类的定义

class是关键字,bases是要继承的父类,默认继承object类。

class 类名(bases):

方法的定义

class 类名(object):
    def 方法1(self,参数列表):
        pass
    def 方法2(self,参数列表):
        pass

创建对象

对象变量 = 类名()
class Cat:   #创建一个类
    def eat(self):   #定义一个eat方法
        print("小猫爱吃饭")
    def drink(self):
        print("小猫爱喝水")

tmo = Cat()  #实例化调用一个对象, 可以调用方法
tmo.eat()
tmo.drink()
print(tom)
# 打印的是引用的对象 是由哪一个类创建的对象,以及在内存中的地址(十六进制表示)

小猫爱吃饭
小猫爱喝水
<__main__.Cat object at 0x0000020507EC41A0>

init初始化方法

当使用 类名()创建对象时,会 自动` 执行以下操作:

  • 为对象在内存中分配空间 --创建对象
  • 为对象的属性设置初始值 --初始化方法( init )
class Cat:
    """
    这是一个猫类
    """
    def __init__(self,new_name):
        print("初始化方法")
        self.name = new_name
    def eat(self):
        print("%s 爱吃鱼" % self.name)

    def drink(self):
        print("%s 爱喝水" % self.name)

tom = Cat("Tom")
print(tom.name)
tom.eat()
tom.drink()

lazy_cat = Cat("大懒猫")
lazy_cat.eat()
lazy_cat.drink()

内置方法和属性

  • 当使用类名()创建对象时,为对象分配完空间后,自动调用__init__方法
  • 当一个对象被从内存中销毁前,会自动调用__del__方法
  • 利用__str__这个内置方法能够打印自定义的内容
class Cat:
    def __init__(self,news_name):
        self.name=news_name
        print("%s 爱吃鱼" % news_name)

    def __del__(self):
        print("%s 我去了" % self.name)

    def __str__(self):
        return '我是小猫 %s' % self.name


tom = Cat("tom")
print(tom)
del tom # del 关键字可以删除一个对象

面向对象封装

  • 封装是面向对象编程的一大特点
  • 面向对象编程的 第一步 – 将 属性 和 方法 封装 到一个抽象的类中
  • 外界 使用 类 创建 对象,然后 让对象调用方法
  • 对象方法的细节 都被 封装 在 类的内部
    重点:一个对象的 属性 可以是 另外一个类创建的对象

场景:士兵突击
需求
1.士兵 许三多 有一把 AK47
2. 士兵 可以 开火
3.枪 能够 发射 子弹
4.枪 装填 装填子弹 --增加子弹数量

枪类

class Gun:
    def __init__(self,model):
        self.model = model

        self.bullet_count = 0

    def add_bullet(self,bullet):
        self.bullet_count = bullet

    def shoot(self):
        if self.bullet_count <= 0:
            print("%s 没有子弹了" % self.model)
            return

        self.bullet_count -= 1
        print("%s tututtutu %d" % (self.model,self.bullet_count))

ak47 = Gun('AK47')
ak47.add_bullet(50)
ak47.shoot()

士兵类
fire 方法需求

1>判断是否有枪,没有枪没法冲锋
2>喊一声口号
3>装填子弹
4>射击

class Gun:
    def __init__(self, model):
        self.model = model

        self.bullet_count = 0

    def add_bullet(self, bullet):
        self.bullet_count = bullet

    def shoot(self):
        if self.bullet_count <= 0:
            print("%s 没有子弹了" % self.model)
            return

        self.bullet_count -= 1
        print("%s tutututu %d" % (self.model, self.bullet_count))

主方法

ak47 = Gun('AK47')

xusanduo = Soldier('许三多')
xusanduo.gun = ak47
xusanduo.fire()
print(xusanduo.gun)

私用属性和私有方法

在定义属性或方法时,在 属性名或者方法名前增加 两个下划线,定义的就是私有属性或方法

lass Women:
    def __init__ (self, name):
        self.name = name
        self.__age = 18
#定义私有方法
    def __secret(self):
        print("%s 的年龄是 %d" % (self.name,self.__age))
        
#私有方法,同样不允许在外界直接访问
xiaofang.secret()

伪私有方法和私有属性
在给 属性、方法 命名时,实际是对 名称 做了一些特殊处理,使得外界无法访问到
处理方式在是在 名称 前面加上_类名 => _类名__属性名

class Women:
    def __init__ (self, name):
        self.name = name
        # 定义私有属性
        self.__age = 18
#定义私有方法
    def __secret(self):
        print("%s 的年龄是 %d" % (self.name,self.__age))
        
#私有属性,同样不允许在外界直接访问 想要访问使用_类名__属性名
print(xiaofang._Women__age)
#私有方法,同样不允许在外界直接访问 想要访问使用_类名__方法名
xiaofang._Women__secret()

单继承

继承的概念:子类 拥有 父类 的所有 方法 和 属性

class Animal(object):
    def eat(self):
        print("吃")

    def drink(self):
        print("喝")

    def run(self):
        print("跑")

    def sleep(self):
        print("睡")


class Dog(Animal):

    def bark(self):
        print("汪汪叫")

class XiaoTianQuan(Dog):
    def fly(self):
        print("我会飞")


xtq = XiaoTianQuan()
xtq.eat()
xtq.drink()
xtq.run()
xtq.sleep()
xtq.bark()
xtq.fly()

方法的重写
子类拥有父类的所有方法和属性子类继承自 父类,可以直接 享受 父类中已经封装好的方法,不需要再次开发。但是当父类 的方法实现不能满足子类需求时,可以对方法进行 重写(override)。

重写父类方法有两种情况:
(1) 覆盖父类的方法
(2) 对父类方法进行扩展

覆盖父类的方法
如果在开发中,父类的方法实现 和 子类的方法实现完全不同。就可以使用 覆盖 的方式,在子类中 重新编写 父类的方法实现 I
具体的实现方式,就相当于在 子类中 定义了一个 和父类同名的方法并且实现重写之后在运行时,只会调用 子类中重写的方法,而不再会调用 父类封装的方法

class Animal(object):
    def eat(self):
        print("吃")

    def drink(self):
        print("喝")

    def run(self):
        print("跑")

    def sleep(self):
        print("睡")
        
class Dog(Animal):
    def bark(self):
        print("汪汪叫")
        
class XiaoTianQuan(Dog):
    def fly(self):
        print("我会飞")
	# 对父类dog的bark方法进行了重写
    def bark(self):
        print("叫的和神一样......")

xtq = XiaoTianQuan()
# 如果子类中,重写了父类的方法
# 在使用子类对象调用方法时,会调用子类中重写的方法
xtq.bark()

对父类方法进行扩展
如果在开发中,子类的方法实现 中 包含 父类的方法实现,父类原本封装的方法实现 是 子类方法的一部分就可以使用 扩展 的方式

  • 在子类中 重写 父类的方法
  • 在需要的位置使用 super().父类方法来调用父类方法的执行
  • 代码其他的位置针对子类的需求,编写 子类特有的代码实现

关于 super(),在Python中super是一个 特殊的类super()就是使用 super 类创建出来的对象。

class XiaoTianQuan(Dog):
    def fly(self):
        print("我会飞")

    def bark(self):
        # 1.针对子类特有的需求,编写代码
        print("神一样的叫唤..")
        # 2.使用 super().调用原本在父类中封装的方法
        super().bark()
        # 3.增加其他子类的代码
        print("$%^$名^#%$号")
xtq = XiaoTianQuan()
xtq.bark()

Python2调用父类方法
在 Python 2.x时,如果需要调用父类的方法,还可以使用以下方式:

父类名.方法(self)

不推荐使用,因为一旦 父类发生变化,方法调用位置的 类名 同样需要修改

class XiaoTianQuan(Dog):
    def fly(self):
        print("我会飞")

    def bark(self):
        # 1.针对子类特有的需求,编写代码
        print("神一样的叫唤..")
        # 2.使用 super().调用原本在父类中封装的方法
        super().bark()
        #父类名.方法(self)
        Dog.bark(self)
        # 调用XiaoTianQuan的子类bark 会形成递归调用,出现死循环
        XiaoTianQuan.baek(self)
        # 3.增加其他子类的代码
        print("$%^$名^#%$号")
xtq = XiaoTianQuan()
xtq.bark()

父类的私有属性和私有方法

  • 子类对象不能在自己的方法内部直接访问父类的私有属性或私有方法
  • 子类对象可以通过父类的公有方法间接访问到私有属性或私有方法

多继承

子类可以拥有多个父类,并且具有所有父类的属性和方法
多继承的语法

class 子类名(父类名1,父类名2..)
	pass
class A:
    def test(self):
        print("Hello World")

class B:
    def demo(self):
        print("Hello")

class C(A, B):
 """多继承可以让子类对象,同时具有多个父类的属性和方法"""
    pass

c = C()
c.test()
c.demo()
  • 如果父类之间 存在 同名的属性或者方法,应该尽量避免 使用多继承
  • Python 中针对类提供了一个内置属性__mro__可以查看方法搜索顺序
# MRO是method resolution order 主要用于在多继承时判断方法、属性的调用路径.
print(c._mro_)

# 返回值
(<class'__main_.c'>,<class'__main__.A'>,<class' __main__.B'>,<class 'object'>)

多态

面向对象三大特性
1.封装 根据 职责 将 属性 和 方法 封装 到一个抽象的 类 中
定义类的准则

2.继承 实现代码的重用,相同的代码不需要重复的编写
设计类的技巧
子类针对自己特有的需求,编写特定的代码

3.多态 不同的 子类对象 调用相同的 父类方法,产生不同的执行结果
多态 可以 增加代码的灵活度
以 继承 和 重写父类方法 为前提
是调用方法的技巧,不会影响到类的内部设计

1.在 Dog类中封装方法 game
	普通狗只是简单的玩耍
	
2.定义 XiaoTianDog继承自Dog,并且重写 game 方法
	哮天犬需要在天上玩耍
	
3.定义 Person 类,并且封装一个 和狗玩 的方法
	在方法内部,直接让 狗对象 调用game 方法
class Dog():
    def __init__(self,name):
        self.name = name

    def game(self):
        print("%s 奔奔跳跳的玩耍" % self.name)



class XiaoTianQuan(Dog):
    def game(self):
        print("%s 哮天犬飞到天上玩耍" % self.name)



class Person:
    def __init__(self,name):
        self.name = name

    def game_with_dog(self,dog):
        print("%s和 %s 在玩游戏" % (self.name,dog.name))
        dog.game()
#不同的 `子类对象` 调用相同的 `父类方法`,产生不同的执行结果
wangcai = Dog("wangcai")
# wangcai = XiaoTianQuan("wangcai")
xiaoming = Person("xiaoming")
xiaoming.game_with_dog(wangcai)

类的结构

实例
使用 类名() 创建对象 的动作有两步:

  • 在内存中为对象 分配空间
  • 调用初始化方法__init__为 对象初始化
  • 对象创建后,内存 中就有了一个对象的 实实在在 的存在 – 实例

概念名称

  • 创建出来的对象叫做类的实例
  • 创建对象的动作叫做实例化
  • 对象的属性叫做实例属性
  • 对象调用的方法叫做实例方法

类是一个特殊的对象

class AAA: #定义的类属于 类对象
	pass	
obj1 = AAA() #属于 实例对象
  • 在程序运行时,类 同样 会被加载到内存
  • 在Python 中,类是一个特殊的对象 – 类对象
  • 在程序运行时,类对象 在内存中 只有一份,使用 一个类 可以创建出 很多个对象实例
  • 除了封装 实例 的 属性 和 方法外,类对象 还可以拥有自己的 属性 和 方法
    • 类属性
    • 类方法
  • 通过 类名. 的方式可以 访问类的属性 或者 调用类的方法
  • 注意,如果使用 对象.类属性 = 值 赋值语句,只会 给对象添加一个属性,而不会影响到 类属性的值
# 定义一个 工具类
# 每件工具都有自己的 name
# 需求 --知道使用这个类,创建了多少个工具对象?

class Tool(object):
    # 使用赋值语句定义类属性,记录所有工具对象的数量
    count =0
    def __init__(self,name):
        self.name = name
    # 让类属性的值+1
        Tool.count += 1

#1.创建工具对象
tool1 = Tool("斧头")
tool2 = Tool("榔头")
tool3 = Tool("水桶")

#2.输出工具对象的总数
print(Tool.count)

类方法

  • 类属性就是针对类对象定义的属性
    • 使用 赋值语句 在 class 关键字下方可以定义 类属性
    • 类属性用于记录与这个类相关的特征
  • 类方法 就是针对 类对象 定义的方法
    • 在类方法内部可以直接访问类属性或者调用其他的类方法

语法如下

@classmethod
def 类方法名(cls):
	pass
  • 类方法需要用修饰器 @classmethod 来标识,告诉解释器这是一个类方法
  • 类方法的 第一个参数 应该是 cls
    • 由 哪一个类 调用的方法,方法内的 cls 就是 哪一个类的引用
    • 个参数和 实例方法 的第一个参数是 self 类似
    • 使用其他名称也可以,不过习惯使用cls
  • 通过 类名. 调用类方法时,不需要传递 cls 参数
  • 在方法内部
    可以通过 cls. 访问类的属性
    也可以通过 cls. 调用其他的类方法
class Tool(object):
    # 使用赋值语句定义类属性,记录所有工具对象的数量
    count =0

    # 定义一个类方法
    @classmethod
    def show_tool_count(cls):  # cls类似于self
        print("工具的总数为 %d " % cls.count)

    def __init__(self,name):
        self.name = name
        # 让类属性的值+1
        Tool.count += 1

#1.创建工具对象
tool1 = Tool("斧头")
tool2 = Tool("榔头")
tool3 = Tool("水桶")
#2.输出工具对象的总数
tool1.show_tool_count()
tool3.show_tool_count()

静态方法

在开发时,如果需要在 类 中封装一个方法,这个方法:

  • 既 不需要 访问 实例属性 或者调用 实例方法
  • 也 不需要 访问 类属性 或者调用 类方法
  • 这个时候,可以把这个方法封装成一个 静态方法

语法如下

@staticmethod
def 静态方法名():
	pass

通过 类名. 调用静态方法

class Dog(object):
    @staticmethod
    def run():
        # 不访问实例属性/类属性
        print("小狗要跑...")

# 通过类名,调用静态方法 - 不需要创建对象
Dog.run()

需求

设计一个 Game 类

属性:
	定义一个 类属性 top_score 记录游戏的 历史最高分
	定义一个 实例属性 player_name 记录 当前游戏的玩家姓名

方法:
	静态方法 show_help 显示游戏帮助信息
	类方法 show_top_score 显示历史最高分
	实例方法 start_game 开始当前玩家的游戏

4.主程序步骤
	1)查看帮助信息
	2)查看历史最高分
	3)创建游戏对象,开始游戏
class Game(object):
    #历史最高分
    top_score =0
    def __init__(self,player_name):
        self.player_name = player_name

    @staticmethod
    def show_help():
        print("帮助信息:让僵尸进入大门")

    @classmethod
    def show_top_score(cls):
        print("历史记录 %d" % cls.top_score)

    def start_game(self):
        print("%s 开始游戏啦..." % self.player_name)

#1.查看游戏的帮助信息
Game.show_help()
#2.查看历史最高分
Game.show_top_score()
#3.创建游戏对象
game = Game("小明")
game.start_game()

内部类

一般情况下不使用内部类,这样会使程序结构复杂,但是理解内部类有助于理解模块的调用。
下面例子中,People类中又定义了Father类和Mother类两个内部类。创建内部类的实例化对象,可以通过外部类的实例化对象调用内部类完成,如Lisi = Zhangsan.Father();也可以直接使用外部类名调用内部类,如Liming = People.Mother()。

class People():
    code = 0

    class Father():
        code = 1

    class Mother():
        code = 2


zhangsan = People()
lisi = zhangsan.Father()  # 第一种实例化方法
print(lisi.code)  # 输出结果:1
liming = People.Mother()  # 第二种实例化方法
print(liming.code)  # 输出结果:2

单例

new 方法
使用类名()创建对象时,Python的解释器首先会调用new_方法为对象分配空间。
__new__是一个 由 object 基类提供的 内置的静态方法,,主要作用有两个:

  • 在内存中为对象分配空间
  • 返回对象的引用
    Python 的解释器获得对象的 引用 后,将引用作为 第一个参数,传递给__init__方法。重写new方法一定要return super().new(cls)。否则 Python 的解释器 得不到 分配了空间的 对象引用,就不会调用对象的初始化方法
    注意:new 是一个静态方法,在调用时需要 主动传递cls 参数
class MusicPlayer(object):
    def __new__(cls,*args,**kwargs):
        # 1.创建对象时,new方法会被自动调用
        print("创建对象,分配空间")
        # 2.为对象分配空间    __new__是静态方法需要手动传参
        instance =super().__new__(cls)
        # 3.返回对象的引用
        return instance

    def __init__(self):
        print("播放器初始化")

# 创建播放器对象
player = MusicPlayer()
print(player)

python中的单例

单例 --让类创建的对象,在系统中只有唯一的一个实例

  • 定义一个类属性,初始值是 None,用于记录单例对象的引用
  • 重写new_方法
  • 如果类属性 is None,调用父类方法分配空间,并在类属性中记录结果
  • 返回类属性中记录的对象引用
class MusicPlayer(object):
	# 记录第一个被创建对象的引用
    instance = None
    def __new__(cls,*args,**kwargs):
        # 1.判断类属性是否为空对象
        if cls.instance is None:
        	#调用父类方法为第一个对象的分配空间
            cls.instance = super().__new__(cls)
        # 2.为对象分配空间    __new__是静态方法需要手动传参

        # 3.返回类属性保存的对象引用
        return cls.instance

# 创建播放器对象
player1 = MusicPlayer()
print(player1)

player2 = MusicPlayer()
print(player2)
<__main__.MusicPlayer object at 0x0000016849A54320>
<__main__.MusicPlayer object at 0x0000016849A54320>

只执行一次初始化工作

在每次使用 类名()创建对象时,Python 的解释器都会自动调用两个方法:

  • new 分配空间O
  • init_对象初始化O

在上一小节对new方法改造之后,每次都会得到第一次被创建对象的引用,但是初始化方法还会被再次调用。

需求:
让初始化动作 只被 执行一次

解决办法:

  • 定义一个类属性 init_flag 标记是否 执行过初始化动作,初始值为 False
  • 在init方法中,判断 init_flag,如果为 False 就执行初始化动作
  • 然后将 init_flag 设置为 True
  • 这样,再次自动调用init_方法时,初始化动作就不会被再次执行了
class MusicPlayer(object):
    # 记录第一个被创建对象的引用
    instance = None
    # 记录初始化执行状态
    init_flag = False

    def __new__(cls, *args, **kwargs):
        # 1.判断类属性是否为空对象
        if cls.instance is None:
            # 调用父类方法为第一个对象的分配空间
            cls.instance = super().__new__(cls)
        # 3.返回类属性保存的对象引用
        return cls.instance


    def __init__(self):
        # 1.判断是否执行过初始化动作
        if MusicPlayer.init_flag:
            return
        # 2.如果没有执行过就执行初始化动作
        print("播放器初始化")
        # 3.修改初始化状态
        MusicPlayer.init_flag = True

# 创建播放器对象
player1 = MusicPlayer()
print(player1)

player2 = MusicPlayer()
print(player2)
播放器初始化
<__main__.MusicPlayer object at 0x000001BB62364500>
<__main__.MusicPlayer object at 0x000001BB62364500>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值