day20笔记 :
  对象的属性管理函数
    getattr(obj, name[,default])
    hasattr(obj, name)
    setattr(obj, name, value)
    delattr(obj, name)
  示例:
    class Dog:
       pass
    d = Dog()
    d.color = "白色"
    c = getattr(d, "color")  # d.color
    hasattr(d,"color") # True
    setattr(d,'kinds', '导盲犬')
    delattr(d, 'kinds')


异常(高级)
  回顾:
    try-except
    try-finally
    raise
    assert

with语句
  语法:
    with 表达式1 [as 变量1],表达式2 [as 变量2], ... :
       语句块
  作用:
    使用对资源进行访问的场合,确保使用过程中,不算是否发生异常,都会执行必须的'清理'操作,并释放资源.
      如: 文件使用后自动关闭,线程中锁的自动获取和释放等(线程后面会学)
  说明:
    with语句并不会改变异常的状态(同try-finally类似)

  示例见:
  

# with.py
try:
    f = open("text.txt", 'w')
    try:
        s = int(input("请输入整数:"))  #故意制造异常

        f.write("hello")
    finally:
        f.close()
except OSError:
    print("文件打开失败")
except: 
    print("读取数据失败")
# with.py
try:
    # 用with语句实现
    # f = open("text.txt", 'w')
    with open("text.txt", 'w') as f:
        s = int(input("请输入整数:"))  #故意制造异常

        f.write("hello")
except OSError:
    print("文件打开失败")
except: 
    print("写入数据失败")

 

 

环境管理器:
  1. 类内有__enter__和 __exit__实例方法的类创建的实例被称为环境管理器
  2. 能够用with进行管理的对象必须是环境管理器
  3. __enter__方法将在进入with语句时被调用,且返回由as 变量 管理的对象
  4. __exit__方法将在离开with语句时被调用,且可以用形参来判断离开with语句时的状态

  示例见:
    

# context.py

# 此示例示意自定义用 with 管理的对象
class A:
    '''此类的对象将可用于with语句中'''
    def __enter__(self):
        print("已经进入到了with语句的内部")
        return self  #把自己返回由as 来绑定
    def __exit__(self, e_t, e_v, e_tb):
        ''' e_t 用来绑定异常类型
            e_v用来绑定异常对象
            e_tb_用来绑定追踪对象
            在没有异常时,三个形参都绑定None
        '''
        if e_t is None:
            print("已正常离开with语句")
        else:
            print("是在出现异常时走异常流程离开的with语句")

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

 


运算符重载 overload
  什么是运算符重载
    让自定义的类生成的对象(实例) 能够使用运算符进行操作
  作用:
    让程序简洁易读
    对自定义的对象将运算符赋序新的规则
  说明:
    运算符重载方法的参数的固定的含义,不建议改变运算符的原的含义

算术运算符重载方法
方法名               运算符和表达式    说明
__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 ) 右手边

示例见:
 

# mynumber.py

# 此示例示意 运算符重载
class MyNumber:
    '自定义数字'
    def __init__(self, value):
        self.data = value

    def __repr__(self):
        return "MyNumber(%d)" % self.data
    
    def __add__(self, other):
        '''加号运算符的重载方法'''
        print("__add__被调用")
        v = self.data + other.data
        obj = MyNumber(v)  # 创建一个新对象
        return obj
    
    def __sub__(self, rhs):
        return MyNumber(self.data - rhs.data)

n1 = MyNumber(100)
n2 = MyNumber(200)
# n3 = n1.__add__(n2)  # MyNumber(300)
n3 = n1 + n2  # 等同于n1.__add__(n2)
print(n1, "+", n2, "=", n3)
n4 = n1 - n2
print(n1, "-", n2, "=", n4)

 

练习:
  实现两个自定义的列表相加操作
    class MyList:
        ...  # 此处代码自己实现
   
    L1 = MyList(range(1, 4))
    L2 = MyList([4, 5, 6])
    L3 = L1 + L2
    print(L3)  # MyList([1, 2, 3, 4, 5, 6])
    L4 = L2 + L1
    print(L4)  # MyList([4, 5, 6, 1, 2, 3])
    L5 = L1 * 3
    print(L5)  # MyList([1,2,3,1,2,3,1,2,3])

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

反向算术运算符重载方法
方法名               运算符和表达式    说明
__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 幂运算
  注: lhs (left hand side ) 左手边

复合赋值算术运算符的重载
  以复合赋值算术运算符 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 幂运算
  注: rhs (right hand side ) 右手边

比较运算符的重载:
方法名               运算符和表达式    说明
__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)rhs  >> 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):
        '''重载负号运算符'''
        # L = [-x for x in self.data]
        # L = (-x for x in self.data)
        L = map(lambda x:-x, self.data)
        return MyList(L)

L1 = MyList([1, -2, 3, -4, 5])
L2 = -L1
print(L2)  # MyList([-1, 2, -3, 4, -5])
# L3 = +L1
# print(L3)  # MyList([1, 2, 3, 4, 5])

 

in / not in 运算符重载
  方法格式:
    def __contains__(self, e):
       ...
 
  #  e in self
示例见:

# 此示例示意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):
        '重载in 运算符,只需要判断 e是否在self里'

        return e in self.data

L1 = MyList([1, -2, 3, -4, 5])
print(3 in L1)  # True
print(3 not in L1)  # False
print(4 not in L1)  # True
print(4 in L1)  # False

 

索引和切片运算符的重载
重载方法:
  方法名            运算符和表达式  说明
 __getitem__(self, i)    x=self[i]   取值
 __setitem__(self, i, v) self[i]=v   赋值
 __delitem__(self, i)    del self[i] 删除

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

# 此示例示意 索引和切片 [] 运算符重载方法
class MyList:
    def __init__(self, iterable=()):
        self.data = list(iterable)

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

    def __getitem__(self, i):
        print("__getitem__被调用, i=", i)
        return self.data[i]

    def __setitem__(self, i, v):
        print("__setitem__: i=", i, "v=", v)
        # print("嘿嘿!")
        self.data[i] = v

    def __delitem__(self, i):
        print("__delitem__被调用 i=", i)
        del self.data[i]

L1 = MyList([1, -2, 3, -4, 5])
x = L1[2]  # x = L1.__getitem__(2)
print(x)
L1[1] = 2.2  # L1.__setitem(1, 2.2)
print(L1)

del L1[3]  # L1.__delitem(3)
print(L1)
# 此示例示意 切片 [] 运算符重载方法
class MyList:
    def __init__(self, iterable=()):
        self.data = list(iterable)

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

    def __getitem__(self, i):
        print("__getitem__被调用, i=", i)
        if type(i) is int:
            print("您正在执行索引操作")
        elif type(i) is slice:
            print("您正在执行切片操作")
            print("起始值:", i.start)
            print("终止值:", i.stop)
            print("步长:", i.step)

        return self.data[i]


L1 = MyList([1, -2, 3, -4, 5])
x = L1[1::2]
print(x)  # [-2, -4]

x = L1[2]
print(x)  # 3

 

slice构造函数:
  作用:
    用于创建一个slice切片对象,此对象存储一个切片的起始值,终止值,步长信息
  格式:
    slice(start=None, stop=None, step=None)
slice的属性:
  s.start  切片的起始值,默认为None
  s.stop  切片的终止值,默认为None
  s.step  切片的步长,默认为None

特性属性 @property
  实现其它语言所拥有的 getter 和 setter 功能

  作用:
    用来模拟(虚拟)一个属性
    通过@property 装饰器可以对模拟的属性赋值和取值加以控制
  示例见:
    

# property.py

# 此示例示意用@property 来实现getter和 setter

class Student:
    def __init__(self, s):
        self.__score = s  # 成绩

    def get_score(self):
        '''getter'''
        return self.__score

    def set_score(self, s):
        '''setter'''
        assert 0 <= s <= 100, "成绩超出范围"
        self.__score = s

s1 = Student(59)
print(s1.get_score())
# print(s1.score)  # 取值
s1.set_score(999999999999)
# s1.score = 999999999999   # 赋值
# property.py

# 此示例示意用@property 来实现getter和 setter

class Student:
    def __init__(self, s):
        self.__score = s  # 成绩

    @property
    def score(self):
        '''getter'''
        print("调用getter")
        return self.__score

    @score.setter
    def score(self, s):
        '''setter'''
        print("调用setter")
        assert 0 <= s <= 100, "成绩超出范围"
        self.__score = s

s1 = Student(59)
print(s1.score)  # 取值
# s1.score = 999999999999 #触发AssertionError
s1.score = 80   # 赋值
print(s1.score)