Skr-Eric的Python课堂(二十二)——Python的异常和运算符重载

异常(高级)

  with 语句

    语法:

      with 表达式1 [as 变量1], 表达式2 [as 变量2], ...:

         语句块

  作用:

    使用于对资源进行访问的场合,确保使用过程中不管是否发生异常都会执行必须的'清理'操作, 并释放资源

      如:文件打开后自动关闭,线程中锁的自动获取和释放等(线程后面会学)

  说明:

    as 子句中的变量用于绑定表达式执行后生成的对象

    with语句并不会改变异常的状态

 

  示例:

# 第一种方式用try-finally保证文件一定能够正常关闭
try:
    f = open("../../day19/day19.txt")
    try:
        for l in f:
            x = int('aaaa')  # 出现异常
            print(l)
    finally:
        f.close()
        print("文件已经关闭")
except OSError:
    print("打开文件失败")

  环境资源管理器

    1. 类内有 __enter__和 __exit__实例方法的类创建的对象被称为环境管理器

    2. 能够用with语句进行管理的对象必须是环境管理器

    3. __enter__方法将在进入with语句时被调用, 由as变量绑定返回的对象

    4. __exit__方法将在离开with语句时被自动调用,且可以通过参数来判断离开with语句时是否有异常发生

 

  示例:

# 此示例示意自定义的对象加入__enter__ 和 __exit__ 方法,让A类的对象能够使用with使用语句
class A:
    '''此类的对象可以用于with语句进行管理'''
    def __enter__(self):
        print("此方法是在with语句内执行的")
        return self  # self将 被 with 中的as 变量绑定

    def __exit__(self, exc_type, exc_val, exc_tb):
        '''exc_type 用来绑定错误类型,当没有异常发生时绑定None
           exc_val 用来绑定错误对象,当没有发生异常时绑定None
           exc_tb 用来绑定TraceBack对象,当没有异常时绑定None
        '''
        if exc_type is None:
            print("您已离开with语句,离开时没有发生任何异常")
        else:
            print("您已离开with语句")
            print("错误类型是:", exc_type)
            print("错误对象是:", exc_val)
            print('Traceback:', exc_tb)

with A() as a:
    print("这是with语句内部的输出")
    int(input("请输入整数: "))

print("程序正常结束")

 

运算符重载

  什么是运算符重载

    让自定义的类生成的对象(实例) 能够使用运行算进行操作

  作用:

    让自定义类的实例像内建对象一样进行运算符操作

    让程序简洁易读

    对自定义对象将运算符赋予新的规则

 

  算术运算符的重载:

    方法名                  运算符和表达式   说明

  __add__(self, rhs)        self + rhs    加法

  __sub__(self, rhs)        self - rhs    减法

  __mul__(self, rhs)        self * rhs    乘法

  __truediv__(self, rhs)    self / rhs    除法

  __floordiv__(self, rhs)   self // rhs   地板除

  __mod__(self, rhs)        self % rhs    取模(求余)

  __pow__(self, rhs)        self ** rhs   幂运算

   

   rhs (right hand side)  右手边

 

  说明:

    运算符重载方法及参数已经有固定的含义,不建议改变原的运算符的含义及参数的意义

 

  二元运算符的重载方法格式:

    def __xxx__(self, other):

        语句块

 

反向算术运算符的重载

  当运算符的左侧为内建类型时,右侧为自定义类型进行算术运算符运算时会出现TypeError错误,因无法修改内建类型的代码实现运算符重载,此时需要使用反向算术运算符重载

 

反向算术运算符的重载:

    方法名                  运算符和表达式   说明

  __radd__(self, lhs)        lhs + self    加法

  __rsub__(self, lhs)        lhs - self    减法

  __rmul__(self, lhs)        lhs * self    乘法

  __rtruediv__(self, lhs)    lhs / self    除法

  __rfloordiv__(self, lhs)   lhs // self   地板除

  __rmod__(self, lhs)        lhs % self    取模(求余)

  __rpow__(self, lhs)        lhs ** self   幂运算

 

示例:

# 此示例示意反向算术运算符的重载
class MyList:
    def __init__(self, iterable=()):
        self.data = list(iterable)

    def __repr__(self):
        return "MyList(%s)" % self.data

    def __add__(self, rhs):
        return MyList(self.data + rhs.data)

    def __mul__(self, rhs):
        '''rhs 为int类型, rhs.data 是不存在的'''
        print("__mul__ 被调用")
        return MyList(self.data * rhs)

    def __rmul__(self, lhs):
        print("__rmul__被调用")
        return MyList(self.data * lhs)

L1 = MyList([1, 2, 3])
L2 = MyList([4, 5, 6])

L3 = 3 * L1
print(L3)

 

复合赋值算术运算符的重载

  以复合赋值算术运算符 x += y 为例, 此运算符会优先调用 x.__iadd__(y) 方法,如果没有__iadd__方法时会将复合赋值运算拆解为: x = x + y 然后调用 x = x.__add__(y) 方法,如果再不存在__add__ 方法则会触发TypeError类型的错误异常

 

复合算术运算符的重载:

    方法名                  运算符和表达式   说明

  __iadd__(self, rhs)        self += rhs    加法

  __isub__(self, rhs)        self -= rhs    减法

  __imul__(self, rhs)        self *= rhs    乘法

  __itruediv__(self, rhs)    self /= rhs    除法

  __ifloordiv__(self, rhs)   self //= rhs   地板除

  __imod__(self, rhs)        self %= rhs    取模(求余)

  __ipow__(self, rhs)        self **= rhs   幂运算

 

比较运算符的重载

    方法名                  运算符和表达式   说明

  __lt__(self, rhs)        self <  rhs   小于

  __le__(self, rhs)        self <= rhs   小于等于

  __gt__(self, rhs)        self >  rhs   大于

  __ge__(self, rhs)        self >= rhs   大于等于

  __eq__(self, rhs)        self == rhs   等于

  __ne__(self, rhs)        self != rhs   不等于

 注: 比较运算符通常返回布尔值True 或 False

 

位相关运算符重载

    方法名                  运算符和表达式   说明

  __and__(self, rhs)        self & rhs   位与

  __or__(self, rhs)         self | rhs   位或

  __xor__(self, rhs)        self ^ rhs   位异或

  __lshift__(self, rhs)     self << rhs  左移

  __rshift__(self, rhs)     self >> rhs  右移

 

反向位运算符重载

    方法名                  运算符和表达式   说明

  __rand__(self, lhs)        lhs & self  位与

  __ror__(self, lhs)         lhs | self  位或

  __rxor__(self, lhs)        lhs ^ self  位异或

  __rlshift__(self, lhs)     lhs << self 左移

  __rrshift__(self, lhs)     lhs >> self 右移

 

复合赋值位相关运算符重载

    方法名                  运算符和表达式   说明

  __iand__(self, rhs)        self &= rhs   位与

  __ior__(self, rhs)         self |= rhs   位或

  __ixor__(self, rhs)        self ^= rhs   位异或

  __ilshift__(self, rhs)     self <<= rhs  左移

  __irshift__(self, rhs)     self >>= rhs  右移

 

一元运算符的重载

  方法名                  运算符和表达式   说明

 __neg__(self)            - self        负号

 __pos__(self)            + self        正号

 __invert__(self)         ~ self        取反

 

 语法:

  class 类名:

      def __xxx__(self):

          ....

  

  示例:

# 此示例示意一元运算符的重载
class MyList:
    def __init__(self, iterable=()):
        self.data = list(iterable)

    def __repr__(self):
        return "MyList(%s)" % self.data

    def __neg__(self):
        G = (-x for x in self.data)
        return MyList(G)

L1 = MyList([1, -2, 3, -4, 5])
L2 = -L1  # <<---此处会有错误
print(L2)  # MyList([-1, 2, -3, 4, -5])

 

in / not in 运算符重载

  格式:

    def __contains__(self, e):

        语句

 

  注: in / not in 返回布尔值 True/False

    当重载了__contains__后, in 和 not in 运算符都可用.

    not in 运算符的返回值与 in 相反

  示例:

# 此示例示意 in / not in 运算符的重载

class MyList:
    def __init__(self, iterable=()):
        self.data = list(iterable)

    def __repr__(self):
        return "MyList(%s)" % self.data

    def __contains__(self, e):
        # print("+++++++++")
        return True if e in self.data else False
        # return e in self.data

L1 = MyList([1, -2, 3, -4, 5])

if 2 in L1:  # 等同于 if L1.__contains__(2):
    print("2在L1内")
else:
    print('2不在L1内')

if 4 not in L1:  # 等同于 if not L1.__contains__(4)
    print("4不在L1内")
else:
    print("4在L1内")

 

索引和切片运算符重载方法:

  方法名                   运算符和表达式       说明

 __getitem__(self, i)     x = self[i]    索引/切片取值

 __setitem__(self, i, v)  self[i] = v    索引/切片赋值

 __delitem__(self, i)     del self[i]  del语句删除索引/切片

 

作用:

  让自定义的类型的对象能够支持索引和切片操作

 

  示例:

from __future__ import unicode_literals

# 此示例示意 in / not in 运算符的重载

class MyList:

    def __init__(self, iterable=()):
        self.__data = list(iterable)

    def __repr__(self):
        return "MyList(%s)" % self.__data

    def __getitem__(self, i):
        '索引取值,i绑定[]内的元素'
        print('i的值是:', i)
        return self.__data[i]  # 返回data绑定列表中的第i个元素

    def __setitem__(self, i, v):
        '''此方法可以让自定义的列表支持索引赋值操作'''
        print("__setitem__被调用, i=", i, 'v=', v)
        self.__data[i] = v

    def __delitem__(self, i):
        self.__data.pop(i)  # del self.__data[i]

L1 = MyList([1, -2, 3, -4, 5])
x = L1[3]  # 能否用索引来访问自定义的MyList类型的对象呢
print(x)

L1[3] = 400  # 索引赋值
print(L1)

del L1[3]
print(L1)  # MyList([1, -2, 3, 5])

# 思考如下语句能执行吗?
print(L1[::2])  # 切片取值
# 此示例示意切片取值操作
class MyList:
    def __init__(self, iterable=()):
        self.__data = list(iterable)

    def __repr__(self):
        return "MyList(%s)" % self.__data

    def __getitem__(self, i):
        print('i的值是:', i)
        if type(i) is int:
            print("用户正在用索引取值")
        elif type(i) is slice:
            print("用户正在用切片取值")
            print("切片的起点是:", i.start)
            print("切片的终点是:", i.stop)
            print("切片的步长是:", i.step)
        elif type(i) is str:
            print("用户正在用字符串进行索引操作")
            # raise KeyError
            return "你想用字符串做什么?"

        return self.__data[i]  # 返回data绑定列表中的第i个元素

L1 = MyList([1, -2, 3, -4, 5])
print(L1[::2])  # 切片取值
print(L1["ABC"])

 

  slice构造函数

    作用:

      用于创建一个slice对象,此对于用于切片操作的传值

    格式:

      slice(start=None, stop=None, step=None)

    slice对象的实例属性:

      start  切片的起始值 默认为None

      stop   切片的终止值 默认为None

      step   切片的步长 默认为None

 

特性属性 @property

  实现其它语言所拥有的getter 和 setter功能

 

  作用:

    用来模拟一个属性

    通过@property装饰器,可以对模拟属性的赋值和取值加以控制

  示例:

from __future__ import unicode_literals
# property.py

class Student:

    def __init__(self, s):
        self.__score = s

    def setScore(self, s):
        '''此方法用设置值加以限制以保证数据的准确性
        setter是用来数据的
        '''
        if 0 <= s <= 100:
            self.__score = s

    def getScore(self):
        '''getter 只用来获取数据'''
        return self.__score

s = Student(50)
s.setScore(100)

# s.score = 100
# print(s.score)
# s.score = 10000
# print(s.score)

 

 

课后习题:

  实现有序集合类OrderSet , 能实现两个集合的交集 &, 并集 |, 补集-, 对称补集 ^, ==,!=,in / not in 等操作

  要求: 集合的内部用list存储数据

s1 = OrderSet([1, 2, 3, 4])
s2 = OrderSet([3, 4, 5])
print(s1 & s2)  # OrderSet([3, 4])
print(s1 | s2)  # OrderSet([1, 2, 3, 4, 5])
print(s1 ^ s2)  # OrderSet([1, 2, 5])
if OrderSet([1, 2, 3]) != OrderSet(1, 2, 3, 4):
    print("不相等")
else:
    print("相等")
if s1 == OrderSet([3, 4, 5]):
    print("s1 == OrderSet([3, 4, 5])")
if 2 in s1:
    print('2 in s1 返回真')
 ... 以下自己测试

 

 

 

想要看更多的课程请微信关注SkrEric的编程课堂

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 可以!一般来说,Python 写小游戏最简单的方式就是使用 Pygame 库,它可以让你创建出有趣的游戏程序。如果你想更深入地学习,可以参考 Python 官方文档中的教程,也可以看一些其他的书籍或教程。 ### 回答2: 好的,我来为您介绍一个简单的Python小游戏——猜数字游戏。 猜数字游戏是一个经典的游戏,在这个游戏中,计算机会随机生成一个数字,玩家需要不断猜测这个数字是多少,直到猜中为止。下面是一个Python实现的示例代码: ```python import random def guess_number(target): guess = None count = 0 while guess != target: guess = input("请输入一个数字:") guess = int(guess) count += 1 if guess < target: print("猜小了,请再试试!") elif guess > target: print("猜大了,请再试试!") else: print("恭喜你,猜对了!你总共猜了{}次。".format(count)) if __name__ == '__main__': target = random.randint(1, 100) guess_number(target) ``` 在这个代码中,我们首先使用`import random`引入random模块,以便随机生成待猜的数字。 在`guess_number`函数中,我们使用一个`while`循环,不断提示玩家输入数字,并将其转换为整数类型。然后与目标数字进行比较,根据猜测的结果提示玩家继续猜测。 当玩家猜中目标数字时,会输出恭喜信息以及玩家猜测的次数。 最后在`if __name__ == '__main__':`部分,我们使用`random.randint(1, 100)`生成1至100之间的随机整数作为目标数字,并调用`guess_number`函数开始游戏。 希望这个简单的示例能帮助您了解如何使用Python编写一个小游戏! ### 回答3: Python是一种广泛应用于软件开发和数据分析的编程语言,也可以用来编写小游戏。以下是一个简单的Python小游戏示例: ```python import random def guess_number(): target = random.randint(1, 100) guess = 0 num_attempts = 0 print("欢迎参加猜数字游戏!") while guess != target: guess = int(input("请输入一个猜测的数字:")) num_attempts += 1 if guess < target: print("猜测的数字太小了!") elif guess > target: print("猜测的数字太大了!") print(f"恭喜你猜对了!你一共猜了{num_attempts}次。") guess_number() ``` 这个小游戏是一个猜数字游戏,程序会随机生成一个1到100之间的数字,玩家需要通过输入数字进行猜测,直到猜对为止。程序会根据玩家的猜测提示猜测的数字是太大还是太小。最后,程序会告诉玩家猜对了,并显示玩家一共猜了多少次。 这只是一个简单示例,你可以根据自己的兴趣和创造力编写更复杂的小游戏。Python的易学易用使其成为编写小游戏的理想选择。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值