Python基础(第八周)

目录

一、类属性和类方法

1.类的结构

2.类属性

3.类方法和静态方法

4.方法的综合案例

二、单例设计模式

1.__new__方法

2.Python中的单例

三、异常

1.异常捕获

2.异常的传递

3.抛出异常

四、文件的基本操作


一、类属性和类方法

1.类的结构

把创建出来对象叫做类的实例,也叫做实例对象,创建对象的动作叫做实例化过程,对象所拥有的属性叫做实例属性,对象调用的方法叫做实例方法,每个对象都有独立的内存空间,多个对象的方法在内存空间中只保留一份,在调用方法时传递对象的引用。

类叫做类对象,创建出来的对象叫做实例对象,在程序运行时,类会和函数一样先加载到内存中但不会去执行。

对象的属性和方法可以使用 self.属性 或 self.方法 的形式去进行调用,类的属性和方法可以通过  类名.属性 或 类名.方法 的形式去进行调用

2.类属性

类属性:就是给类对象定义的属性,通过记录跟类相关的特征,不会去记录对象的特征

需求:定义一个工具类,需要利用这个类去创建对象,现在我们需要记录利用这个类创建了多少对象

class Tools:
    # 定义类属性,记录工具对象的总数
    count = 0

    def __init__(self, name):
        self.name = name
        Tools.count += 1


tool1 = Tools('剪刀')
tool2 = Tools('斧头')
tool3 = Tools('榔头')

print(f'用这个工具类创建了{Tools.count}个工具')  # 用这个工具类创建了3个工具

我们为什么不用:tool1.count 或者 tool2.count 或者 tool3.count 去调用count属性呢?

class Tools:
    # 定义类属性,记录工具对象的总数
    count = 0

    def __init__(self, name):
        self.name = name
        Tools.count += 1


tool1 = Tools('剪刀')
tool2 = Tools('斧头')
tool3 = Tools('榔头')

print(f'用这个工具类创建了{tool1.count}个工具')  # 用这个工具类创建了3个工具

我们可以看到当没有定义count的实例属性时,这种方法是可以的,但是:

class Tools:
    # 定义类属性,记录工具对象的总数
    count = 0

    def __init__(self, name):
        self.name = name
        self.count = 100
        Tools.count += 1


tool1 = Tools('剪刀')
tool2 = Tools('斧头')
tool3 = Tools('榔头')

print(f'用这个工具类创建了{tool1.count}个工具')  # 用这个工具类创建了100个工具
print(f'用这个工具类创建了{Tools.count}个工具')  # 用这个工具类创建了3个工具

 当我们定了一个count的实例属性时,tool1.count 或者 tool2.count 或者 tool3.count 去调用count属性只会调用到实例属性,但通过Tools.count方法就可以调用的类属性

3.类方法和静态方法

类方法

class Tools:
    # 定义类属性,记录工具对象的总数
    count = 0

    def __init__(self, name):
        self.name = name
        Tools.count += 1

    @classmethod
    def count_tools(cls):
        print(f'用这个工具类创建了{cls.count}个工具')


tool1 = Tools('剪刀')
tool2 = Tools('斧头')
tool3 = Tools('榔头')

Tools.count_tools()

count_tools就是一个类方法,在上面加一个classmethod的装饰器就变成了类方法,cls可以换成别的变量名称(习惯用cls),但是他都指向类Tools的引用

静态方法

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

    def run(self):
        print('run')


wangcai = Dog('旺财')

Dog类中的run方法,既没有调用实例属性和实例方法,也没有调用类属性和类方法,这种方法就叫做静态方法,只不过我们通常不写成上面那种形式,而是写成下面这种形式

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

    @staticmethod
    def run():
        print('run')


wangcai = Dog('旺财')

需要注意的是:在方法上面加入一个名字是staticmethod的装饰器,()中不需要写self

调用方法:类名.方法名

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

    @staticmethod
    def run():
        print('run')


wangcai = Dog('旺财')
Dog.run()  # run

4.方法的综合案例

需求:设计一个Game类,属性:定义一个类属性top_score记录游戏的历史最高分,定义一个实例属性player_name记录当前游戏的玩家姓名,方法:静态方法show_helper显示游戏帮助信息,类方法show_top_score显示历史最高分,实例方法start_game开始当前玩家的游戏,主程序步骤:①.查看帮助信息,②.查看历史最高分,③.创建游戏对象,开始游戏

import random


class Game:
    top_score = 0

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

    @classmethod
    def show_top_score(cls):
        print(f'游戏的最高分是{cls.top_score}')

    @staticmethod
    def show_help():
        print('请消灭全部敌人')

    def start_game(self):
        print(f'{self.name}开始游戏...')

        score = random.randint(0, 100)
        if score > Game.top_score:
            Game.top_score = score


# 1.查看帮助信息
Game.show_help()
# 2.查看历史最高分
Game.show_top_score()
# 3.创建游戏对象,开始游戏
xiaoming = Game('小明')
xiaoming.start_game()
# 4.查看游戏过后的最高分
Game.show_top_score()

Tips:当我们需要使用类属性时,就创建类方法;当我们需要使用实例属性,就创建实例方法;当我们既不需要使用类属性也不需要访问实例属性就创建静态方法;当急需要使用实例属性有需要使用类属性时,创建实例方法。

二、单例设计模式

由同一个类创建出来的对象在内存中的地址是相同的(例如:无论我们双击多少次回收站只能打开一个回收站界面)

1.__new__方法

__new__方法是为对象分配内存空间的

class MusicPlayer:

    def __new__(cls, *args, **kwargs):
        pass

    def __init__(self):
        print('音乐播放器初始化成功!')


mp1 = MusicPlayer()
mp2 = MusicPlayer()
print(mp1)  # None
print(mp2)  # None

通过上述代码,我们可以看出对象并没有创建成功,这是因为在创建对象时,首先执行new方法为对象分类内存空间,然后对象在拿着内存空间去执行init方法;但是上述代码中我们并没有为对象返回一个内存空间,所以会创建失败。

class MusicPlayer:

    def __new__(cls, *args, **kwargs):
        return super().__new__(cls)

    def __init__(self):
        print('音乐播放器初始化成功!')


mp1 = MusicPlayer()
mp2 = MusicPlayer()
'''
音乐播放器初始化成功!
音乐播放器初始化成功!
'''
print(mp1)  # <__main__.MusicPlayer object at 0x000001F3A0037DC0>
print(mp2)  # <__main__.MusicPlayer object at 0x000001F3A0037DC0>

2.Python中的单例

class MusicPlayer:
    # 定义类属性记录记录单例对象的引用
    instance = None

    def __new__(cls, *args, **kwargs):
        # 判断类属性是否已经赋值
        if cls.instance is None:
            cls.instance = super().__new__(cls)

        # 返回类属性中单例的引用
        return cls.instance

    def __init__(self):
        print('音乐播放器初始化成功!')


mp1 = MusicPlayer()
mp2 = MusicPlayer()
'''
音乐播放器初始化成功!
音乐播放器初始化成功!
'''
print(mp1)  # <__main__.MusicPlayer object at 0x000001CB44BB3EB0>
print(mp2)  # <__main__.MusicPlayer object at 0x000001CB44BB3EB0>

我们定义的类属性记录单例对象的内存地址,如果第一次创建对象,则会给instance对象赋值为创建的对象的内存地址,如果不是第一次创建对象,此时instance的值是记录的第一次创建对象的内存地址,然后将这个内存地址返回给后面创建的对象,这就可以实现创建的对象的内存地址相同,实现了单例。

但是,上述单例设计还存在一个问题:无论我们创建几个对象,应该只执行一次初始化方法,所以我们要对单例进行改进。

class MusicPlayer:
    # 定义类属性记录记录单例对象的引用
    instance = None

    # 定义类属性判断是否执行过初始化动作
    init_flag = False

    def __new__(cls, *args, **kwargs):
        # 判断类属性是否已经赋值
        if cls.instance is None:
            cls.instance = super().__new__(cls)

        # 返回类属性中单例的引用
        return cls.instance

    def __init__(self):
        # 判断是否执行过初始化动作
        if not MusicPlayer.init_flag:
            print('音乐播放器初始化成功!')
            MusicPlayer.init_flag = True


mp1 = MusicPlayer()
mp2 = MusicPlayer()
'''
音乐播放器初始化成功!
'''
print(mp1)  # <__main__.MusicPlayer object at 0x000001CB44BB3EB0>
print(mp2)  # <__main__.MusicPlayer object at 0x000001CB44BB3EB0>

三、异常

当python接触器碰见代码中的一些错误时会停止运行,并报出一些提示信息,这就是异常

1.异常捕获

我们为什么要进行异常捕获呢?因为有时候我们不想在异常发生时结束程序的运行,而是希望他继续执行后面的语句。

在python代码中我们使用try,except关键字进行异常的捕获

例子:当我们用python代码实现输入被除数和除数,然后求他们的商时,可能会出现几种错误呢?

1.我们输入的不是数字

2.0不能做除数

简单处理:

c = 'hello world'
try:
    a = int(input('请输入被除数:'))
    b = int(input('请输入除数:'))
    c = a / b
except:
    print('请输入正确的数字!')
print(c)

上面的代码无论是碰见了第一种错误还是第二种错误都会输出:请输入正确的数字

所以我们可以这么理解:try中的代码就是要进行异常捕获的代码,except中的代码就是无论try中的代码有什么问题都会被执行

如果我们想针对两种不同的问题进行不同的异常捕获呢?

c = 'hello world'
try:
    a = int(input('请输入被除数:'))
    b = int(input('请输入除数:'))
    c = a / b
except ValueError:
    print('请输入正确的数字!')
except ZeroDivisionError:
    print('0不能做除数!')
except Exception as e:
    print('未知错误:', e)
print(c)

这段代码就是对上面的代码进行了改进,except后面可以加异常名称,表示当遇到这种异常就执行下面的代码

当遇到输入的内容不是数字的错误(ValueError)时:输出请输入正确的数字!

当遇到输入的除数是0(ZeroDivisionError)时:输出0不能做除数!

当遇到我们没有规定的异常时:输出未知错误+错误的名称

异常捕获的完整语法

c = 'hello world'
try:
    a = int(input('请输入被除数:'))
    b = int(input('请输入除数:'))
    c = a / b
except ValueError:
    print('请输入正确的数字!')
except ZeroDivisionError:
    print('0不能做除数!')
except Exception as e:
    print('未知错误:', e)
else:
    print('没有出现错误!')
finally:
    print('异常捕获已结束!')
print(c)

else:没有异常时执行的代码

finally:异常捕获结束时执行的代码(不管有没有异常都会执行的代码)

2.异常的传递

def demo1():
    return int(input('请输入一个数字:'))


def demo2():
    return demo1()


print(demo2())

当我们输入的不是一个数字时,他会报以下错误:

Traceback (most recent call last):
  File "C:/Users/张洲/Desktop/新建文件夹/新建 JetBrains PyCharm.py", line 9, in <module>
    print(demo2())
  File "C:/Users/张洲/Desktop/新建文件夹/新建 JetBrains PyCharm.py", line 6, in demo2
    return demo1()
  File "C:/Users/张洲/Desktop/新建文件夹/新建 JetBrains PyCharm.py", line 2, in demo1
    return int(input('请输入一个数字:'))
ValueError: invalid literal for int() with base 10: 'asd\\'

traceback:回溯

首先将demo1,demo2函数加载到内存是没有错误的,当执行print(demo2())时,他会调用demo2函数,而demo2函数返回的是demo1函数的调用,demo1函数调用时返回int(input('请输入一个数字:')),但是我们输入的不是数字,所以他会报错,demo1的这条语句有错误,会把错误反馈给他的调用者:函数demo2,然后demo2再把错误返回给他的调用者,然后反馈给我们,所以就会出现上面这种回溯式的报错信息。

那么这种情况怎么进行异常捕获呢?

我们通俗的做法是将异常捕获写在主程序中,而不是写在函数里

def demo1():
    return int(input('请输入一个数字:'))


def demo2():
    return demo1()


try:
    print(demo2())
except ValueError:
    print('请输入正确的数字!')
except Exception as e:
    print('未知错误:', e)

3.抛出异常

当我们注册账号时,系统会让我们输入要注册的用户名它通常有一定的长度限制,当我们输入的用户名过长或者过短时,他会提醒我们用户名过短或者用户名过长,这就是典型的抛出异常

def input_name():
    # 1.提示用户输入用户名
    user_name = input('请输入要注册的用户名:')

    # 2.判断用户名的长度
    if 6 <= len(user_name) <= 12:
        return user_name

    # 3.创建异常对象
    ex = Exception('用户名长度应在6-12位之间!')

    # 4.抛出异常
    raise ex


input_name()

当我们输入的用户名不满足<=12,>=6时,抛出异常:

Traceback (most recent call last):
  File "C:/Users/张洲/Desktop/新建文件夹/新建 JetBrains PyCharm.py", line 16, in <module>
    input_name()
  File "C:/Users/张洲/Desktop/新建文件夹/新建 JetBrains PyCharm.py", line 13, in input_name
    raise ex
Exception: 用户名长度应在6-12位之间!

可以看见这次抛出的异常并不是python规定的异常,而是我们自己规定的

我们可以再结合异常捕获

def input_name():
    # 1.提示用户输入用户名
    user_name = input('请输入要注册的用户名:')

    # 2.判断用户名的长度
    if 6 <= len(user_name) <= 12:
        return user_name

    # 3.创建异常对象
    ex = Exception('用户名长度应在6-12位之间!')

    # 4.抛出异常
    raise ex


try:
    user_name = input_name()
except Exception as e:
    print('发现错误:', e)  # 发现错误: 用户名长度应在6-12位之间!

四、文件的基本操作

打开文件:

f = open('文件名的路径','访问方式')

open函数会返回一个对象,我们用一个变量接收(通常用f),open函数第一个变量是文件的路径,比如存在d盘my_info文件夹中的info.txt文件,我们需要写的参数就是D:\my_info\info.txt

当我们不指定访问方式时,默认以 r(只读)方式打开

访问方式说明
r只读方式打开文件,文件的指针将会放在文件的开头,这是默认模式,如果文件不存在,抛出异常
w只写方式打开文件,如果文件存在则会被覆盖,如果文件不存在,创建新文件
a追加方式打开文件,如果文件已存在,文件指针将会放在文件的结尾,如果文件不存在,创建新文件进行写入
r+读写方式打开文件,文件的指针将会放在文件的开头,如果文件不存在,抛出异常
w+读写方式方式打开文件,如果文件存在会被覆盖,如果文件不存在,创建新文件
a+读写方式打开文件,如果文件存在,文件指针将会放在文件的结尾,如果文件不存在,创建新文件进行写入

读取:

读取全部:

打开文件时光标在开头,读取后光标在末尾,此时如果在执行一次read函数,读取不到任何内容

text = f.read()
print(text)

 读取一行:

text = f.readline()
print(text)
# 读取大文件
f = open('文件路径', 'w')
while True:
    text = f.readline()
    if not text:
        break
    # 文件的每一行结尾已经有了\n,所以输出不需要换行
    print(text, end='')
f.close()

 打开文件后必须要关闭:

f.close()

案例:复制文件

# 复制小文件
f_read = open('原文件1.txt', 'r')
f_write = open('原文件1[复制].txt', 'w', encoding='utf-8')

text = f_read.read()
f_write.write(text)

f_read.close()
f_write.close()

# 复制大文件
f_read = open('原文件2.txt', 'r')
f_write = open('原文件2[复制].txt', 'w', encoding='utf-8')

while True:
    text = f_read.readline()
    if not text:
        break
    f_write.write(text)

f_read.close()
f_write.close()

文件操作的习惯写法:

with open('1.txt', 'w', encoding='utf-8') as f:
    f.write('要写入的内容')

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不懂编程的大学生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值