面向对象

面向对象函数式编程 

#狗的特征
dog1 = 
{'name' : '冀祥'
'gender' : '母'
'type' : '柯基'}
dog2 = 
{'name' : '刘鹏'
'gender' : '母'
'type' : '法斗'}
dog3 = 
{'name' : '姚宇轩'
'gender' : '公'
'type' : '泰迪'}

def dog(name,gender,type):            #这是一个类
    def jiao(dog):                    #这是类的函数属性
        print('有一条狗[%s]在汪汪汪' % dog['name'])
    def chi_shi(dog):                 #这是类的函数属性
        print('有一条[%s]在吃屎' % dog['type'])
    def init(name,gender,type):       #此步骤为初始化函数,值为传来的参数,key为新的参数,防止有默认值
        d1 = {'name' : name,
        'gender' : gender,
        'type' : type,
        'jiao' : jiao,
        'chi_shi' : chi_shi}
        return d1
    return init(name,gender,type)     #直接返回实例的信息
dog1 = dog('冀祥','母','柯基')         #实例化
dog1['jiao'](dog1)                    #用实例调用类的方法,用实例本身当作参数传入
dog1['chi_shi'](dog1)            
def school(name,addr,type):
    def init(name,addr,type):
        sch = {
            'name' : name,
            'addr' : addr,
            'type' : type,
            'examination' : examination,
            'recruit_students' : recruit_students
        }
        return sch
    def examination(school):
        print('%s 学校正在考试' %school['name'])
    def recruit_students(school):
        print('%s学校 %s 正在招生' %(school['type'],school['name']))
    return init(name,addr,type)

s1 = school('剑桥','England','私立')
print(s1)
print(s1['name'])
s1['recruit_students'](s1)

s2 = school('哈佛','America','公立')
print(s2['name'])
s2['examination'](s2)

面向对象独有语法class

isinstance(obj,cls) 查询一个对象是由哪个类产生的,返回布尔值。

判断s是否是字符串类型

s = 'harvey'
print(isinstance(s, str))    # True

issubclass(sub,super) 查看前一个类是否是后一个类的子类,返回布尔值。

class Dog:            #正规格式为首字字母大写
    def __init__(self,name,gender,type): #self为默认参数,实则代表实例名
        self.name = name        #dog1.xingming = name
        self.gender = gender    #dog1.xingbie = gender
        self.type = type        #dog1.leixing = type
    def bark(self):
        print('一条叫[%s]的[%s]不停地叫唤' %(self.name,self.type))
    def bite(self):
        print('[%s]又咬人了' %(self.name))
    def chi_shi(self):
        print('一条[%s]正在吃屎' %(self.type))
dog1 = Dog('冀祥','female','柯基')
print(dog1.__dict__) #以字典形式
dog2 = Dog('刘鹏','male','藏獒')
dog3 = Dog('宇轩','male','京巴')
# dog1.bark()
# dog2.bite()
dog3.chi_shi()             #用.的方式调用本质上就是在自己的属性字典中中寻找函数属性,相当于:
Dog.__dict__['chi_shi'](dog3)
# print(Dog.__dict__['bite'](dog1))
print(Dog.__name__)
print(Dog.__doc__)
print(Dog.__module__)      #__main__,代表文件名

 类属性增删改查

class Chinese:
    country = 'China'           #新建一个实例,即数据属性
    def __init__(self,name):
        self.name = name
    def play_games(self,ball):
        print('%s 正在打%s' %(self.name))
#查看
print(Chinese.country)
#修改
Chinese.country = 'America'
print(Chinese.country)  #America
p1 = Chinese('harvey')
print(p1.__dict__)  #{'name': 'harvey'}
print(p1.country)   #America
#增加
Chinese.addr = 'Asian'
print(Chinese.addr)#Asian
print(p1.addr)#Asian
#删除
del Chinese.addr
del Chinese.country
print(Chinese.country) #报错

def have_breakfast(self,food):
    print('%s 正在吃 %s' %(self.name,food))
Chinese.food = have_breakfast #将一个函数添加到Chinese类中成为他的函数属性
print(Chinese.__dict__)       #此时已经在Chinese的字典属性中
p1.food('提拉米苏')
def test(self):
    print('我在玩泥巴')
Chinese.play_games = test     #覆盖之前的play_games
p1.play_games()               #p1.Chinese.play_games(p1) 默认传入p1到self位置

实例属性增删改查

class Chinese:
    country = 'China'           #新建一个实例,即数据属性
    def __init__(self,name):
        self.name = name
    def play_games(self,ball):
        print('%s 正在打%s' %(self.name))
p1 = Chinese('harvey')
print(Chinese.__dict__)
#查看
print(p1.name)
print(p1.play_games)
#增加
p1.age = 18            #增加数据属性
print(p1.__dict__)     #{'name': 'harvey', 'age': '18'}
print(p1.age)
#删除
del p1.age
print(p1.age) #报错
#修改
#不要修改底层的属性字典
#p1.__dict__['gender'] = 'female'
p1.age = 19
print(p1.age)   #19
class Chinese:
    country='China'
    l=['a','b']
    def __init__(self,name):
        self.name=name

    def play_ball(self,ball):
        print('%s 正在打 %s' %(self.name,ball))

p1=Chinese('alex')
print(p1.l)         #['a', 'b']
p1.l=[1,2,3]
print(Chinese.l)    #['a', 'b']
print(p1.__dict__)  #{'name': 'alex', 'l': [1, 2, 3]}
p1.l.append('c')    #用实例调用来l,添加字符串'c',但并不会改变类的l
print(p1.__dict__)  #{'name': 'alex', 'l': [1, 2, 3, 'c']},实例的字典属性中添加成功
print(Chinese.l)    #['a', 'b']但类中不改变

封装

class Room:
    tag = 1
    def __init__(self,name,owner,width,length,heigh):
        self.name = name
        self.owner = owner
        self.width = width
        self.length = length
        self.heigh = heigh

#   def area(self):
#       print('%s住的%s面积是%s' %(self.owner,self.name,self.width*self.length))
#r1 = Room('海景房','harvey',100,80,10)
#r1.area()         #r1.area 封装后不需要括号
    @property       #封装
    def area(self):
        return self.width*self.length
r1 = Room('海景房','harvey',100,80,10)
print(r1.area)      #加了返回值可以实现用户或别人这样使用你的程序,封装逻辑
print(r1.name)      #使你的程序更有神秘感,无法被看破,调用形式都相同

#那如果我们不想通过实例,直接访问类的详情信息呢?
    def test(self):
#       print('这是test函数',self.name)
# print(Room.tag)
# Room.test()     #报错,因为类调用类自己的函数属性必须通过实例
# Room.test(1)    #1.name,随便传一个参数也报错
    @classmethod  #类可以直接调用,不需要实例了。用于解决类调用自己的方法而不借助实例
    def tell_info(cls): #cls相当于接收了一个类名
        print(cls)  #<class '__main__.Room'>
        print(cls.tag)
#Room.tell_info() #1,可以直接调用类的方法了

#工具包,既不跟实例捆绑,也不跟类捆绑,完全脱离的方法,只为类提供服务
    @staticmethod
    def take_shawer(a,b,c):
        print('%s %s %s正在一起洗澡' %(a,b,c))
Room.take_shawer('我','你','她')

组合 

class School:
    def __init__(self,name,addr):
        self.name = name
        self.addr = addr
    def recruit(self):
        print('%s 正在招生' %self.name)
class Course:
    def __init__(self,name,price,school):
        self.name = name
        self.price = price
        self.school = school
s1 = School('剑桥','England')
s2 = School('剑桥','America')
# c1 = Course('English','30k','剑桥','England')#报错,位置参数过多
c1 =Course('English','30k',s1)
print(c1.school.name)
print(s2.name)
print(c1.name)
s2.recruit()
class School:
    def __init__(self,name,addr):
        self.name = name
        self.addr = addr
    def recruit(self):
        print('%s 正在招生' %self.name)
class Course:
    def __init__(self,name,price,school):
        self.name = name
        self.price = price
        self.school = school
s1 = School('剑桥','England')
s2 = School('剑桥','America')
s3 = School('剑桥','Australia')

msg = '1 剑桥 Endland,2 剑桥 America,3 剑桥 Australia'
while True:
    print(msg)
    menu = {
        '1':s1,
        '2':s2,
        '3':s3
    }
    choice = input('选择学校:')
    school_obj = menu[choice] #choice为key,此处取对应的value
    name = input('选择课程:')
    price = input('课程费用:')
    new_course = Course(name,price,school_obj)
    print('课程[%s]属于[%s],费用是[%s]' %(new_course.name,new_course.school.name,new_course.price) )

继承

class Dad:
    money = 10
    def __init__(self,name):
        print('这是爸爸')
        self.name = name
    def teach(self):
        print('%s正在教育他儿子%s' %self.name)
class Son(Dad):
    money = 999999
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def teach(self):
        print('来自子类')
print(Son.money)    #999999
Son.teach(2)        #来自子类
s1 = Son('harvey',18)
s1.teach()          #来自子类
print(s1.money)     #999999
print(s1.name)      #harvey
print(Dad.money)    #10

 子类调用父类的方法

class Vehicle:
    Country = 'China'
    def __init__(self,name,speed,load,power):
        self.name = name
        self.speed = speed
        self.load = load
        self.power = power
    def run(self):
        print('(爸爸)出发啦')

class Subway(Vehicle):
    def __init__(self,name,speed,load,power,line):
        super().__init__(name,speed,load,power)#super(__class__,self).__init__(name,speed,load,power)
        #效果相同 super(Subway,self).__init__(name,speed,load,power)
        self.line = line
    def tell_info(self):
        print(self.name,self.speed,self.load,self.power,self.line)
    def run(self):
        super().run()   #Vehicle.run(self)也可以,但不实用。这是父类的run
        print('(儿子)%s %s 出发啦' %(self.name,self.line)) #这是子类的run
四号线 = Subway('北京地铁','10km/s','10万','电力','4')
四号线.run()
四号线.tell_info()
#接口继承
import abc
class All_file(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def read(self):
        print('All_file read')
    @abc.abstractmethod
    def write(self):
        print('All_file write')
class Disk():
    def read(self):
        print('Disk read')
    def write(self):
        print('Disk write')
class Cdrom():
    def read(self):
        print('Cdrom read')
    def write(self):
        print('Cdrom write')
class Men():
    def read(self):
        print('Men read')
    def write(self):      #若此类没有写入方法,在没有abc模块的约束下,程序也能运行
        print('Men write')#abc模块帮我们规范各类的方法,此类必须要跟上类有共同方法,不然在实例化时会报错
m1 = Men()
m1.read()
m1.write()

继承顺序参考https://www.cnblogs.com/zihe/p/7122055.html

多态 

不同的类在继承了 一个相同的基类之后调用了基类的同一个方法,这个实现过程叫多态(多态的基础实际就是继承)

class H2O:
    def __init__(self,name,temperature):
        self.name = name
        self.temperature = temperature
    def evolve(self):
        if self.temperature < 0:
            print('%s 温度太低结成了冰' %self.name)
        elif self.temperature > 0 and self.temperature < 100:
            print('%s 温度刚好我还是水' %self.name)
        elif self.temperature >= 100:
            print('%s 温度太高了成水蒸汽了' %self.name)
class Water(H2O):
    pass
class Ice(H2O):
    pass
class Vapor(H2O):
    pass
h1 = Water('水', 50)
h2 = Ice('冰', -8)
h3 = Vapor('水蒸汽', 108)
# h1.evolve()
# h2.evolve()
def func(obj):        # 接口函数
    obj.evolve()
func(h1)

封装 

class People:
    leader = 'Rick'
    _leader = 'Daryl'   # 单下划线是一种语言约定,代表创造者不希望使用者访问这个数据
    __leader = 'Negan'  # 双下划线的数据python会重命名,调用的方法也不同
    def __init__(self,name,age,id):
        self.name = name
        self.age = age
        self.id = id
    def get_id(self):
        print(self.id)      # 属于创造者开的接口,可以直接访问id
    def get_leader(self):   # 这是专门用来访问__leader的接口
        print(self.__leader)
print(People._leader)       # 只是一种约定,仍然可以拿到
p1 = People('harvey','18','0414')
p1.get_id()
p1.get_leader()
# 双下划线的那位哥究竟被重命名为什么了呢?
print(p1._People__leader)   # 这样就可以调到了,实际上还是只是语言约定
# 加了下划线将无法通过import *拿到,但可以import _leader
class Room:
    def __init__(self,name,owner,width,length,high):
        self.name=name
        self.owner=owner
        self.__width=width
        self.__length=length
        self.__high=high

    def tell_area(self): #此时我们想求的是面积
        return self.__width * self.__length

    def tell_high(self):
        return self.__high

r1=Room('卫生间','harvey',100,100,10000)
# 此时使用者需求是求面积
print(r1.tell_area())
# 若使用者有新需求,要求体积了,而此时长宽高这些属性已经被我们做成私有的了
# 那我们就只能再定义一个专门计算体积的接口函数或者专门获取tell_high的接口函数供他使用了
# 若我们错误的理解封装的意义,将使用者会频繁使用到的属性封装起来,后期我们只有通过定义无数个接口函数来解决了
# 我们的程序也将被打的千疮百孔

反射以及动态导入

class Mediator:
    features = 'ugly'
    def __init__(self,name,addr):
         self.name = name
         self.addr = addr
    def sell_house(self):
        print('[%s] 在卖房子呢,买不起啊' % self.name)
    def rent_out(self):
        print('[%s] 在出租房子呢,现在都直接买了谁租' % self.name)
# hasattr 判断
print(hasattr(Mediator,'features'))  # True
m = Mediator('五角大楼','美国')
# m.name = m.__dict__['name']
print(hasattr(m, 'name'))  # True
print(hasattr(m, 'sell_house'))  # True判断能否使用某个方法

# getattr 查看
print(getattr(m, 'name'))   # 五角大楼
print(getattr(m, 'rent_out'))   # 内存地址
func = getattr(m, 'rent_out')
func()  # [五角大楼] 在出租房子呢,现在都直接买了谁租
# print(getattr(m,'rent_ou')) #没有则报错
print(getattr(m, 'rent_ou', '没有这个属性'))  # 没有这个属性

# setattr 修改
setattr(m, 'sb', True)
print(m.__dict__)   # {'name': '五角大楼', 'addr': '美国', 'sb': True}
setattr(m, 'name', 'SB')
print(m.__dict__)   # {'name': 'SB', 'addr': '美国', 'sb': True}
setattr(m,'func1', lambda self:self.name+'sb')
print(m.__dict__)   # {'name': 'SB', 'addr': '美国', 'sb': True, 'func1': <function <lambda> at 0x0000022344E43E18>}

# delattr 删除
delattr(m, 'func1')
print(m.__dict__)   # {'name': 'SB', 'addr': '美国', 'sb': True}
delattr(m, 'sb')
print(m.__dict__)   # {'name': 'SB', 'addr': '美国'}
del m.addr
print(m.__dict__)   # {'name': 'SB'}
# 这位类的创建者出去度蜜月了,只留下了一半未完成的代码
class FtpClient:
    'ftp客户端,但是还么有实现具体的功能'
    def __init__(self,addr):
        print('正在连接服务器[%s]' %addr)
        self.addr=addr
    def put(self):
        print('正在上传文件')

# 这位哥想调用一下客户端的类,但是那位哥并没有完成,为了不影响当前工作进度,有了以下代码
from FTP_Client.FTP import FtpClient
f1=FtpClient('8.8.8.8')
# f1.put()  显然这样写会报错
if hasattr(f1,'put'):
    func_get = getattr(f1,'put')    # 这样判断如果put方法完成了,程序就能一路执行下去
    func_get()
else:
    print('其他的逻辑')              #如果被调用的方法并没有完成,也可以不报错地执行我自己的任务,等到那位哥回来了,也不用改动代码

# 动态导入其实就是根据反射来实现的
# 动态导入第一种
module_h = __import__('h.hy')
# print(module_h) # <module 'h' from 'C:\\Users\\HY\\PycharmProjects\\untitled\\h\\__init__.py'>
module_h.hy.test1() # 这是test1

from h.hy import *
test1()
# test2()    *无法调用带下划线的_test2()
from h.hy import test1,_test2   #但这样可以调用
_test2()
# 动态导入第二种
import importlib
h = importlib.import_module('h.hy')
print(h)
h.test1()
h._test2()

类的内置方法

attr系列 

# 以get为例,当访问不存在的方法时系统会自动触发__getattr__,会报错。
# 当我们自己定义之后,会覆盖系统内置的这个函数,执行我们自定义的方法,不变的是当你访问不存在的方法时,同样还是会触发__getattr__
# 此时就能运行我们自定义的逻辑了
# __getattr__
class Num:
    x = 1
    def __init__(self,y):
        self.y = y
    def __getattr__(self, item):
        print('__getattr__执行了')  # 此处是为了表现__getattr__在内部运行了
n1 = Num(8)
print(n1.y)
n1.sss                             # __getattr__执行了,在调用不能调用的方法时才会触发
# __delattr__
class Num:
    x = 1
    def __init__(self,y):
        self.y = y
    def __delattr__(self, item):
        print('__delattr__执行了')  # 此处是为了表现__delattr__在内部运行了
        # del self.item    无限递归
        self.__dict__.pop(item)    #解决递归问题
n1 = Num(8)
del n1.y                           # __delattr__执行了
# __setattr__
class Num:
    x = 1
    def __init__(self,y):
        self.y = y
    # 将所有key换成value
    def __setattr__(self, key, value):
        print('__setattr__执行了')  # 此处是为了表现__setattr__在内部运行了
        # self.key = value  参照len(str)原理是str__len__,即setattr('key','value')
        # 换一次就会执行一次,执行一次就会换一次,将会递归上限
        self.__dict__[key] = value
        # 修改的原理也是通过__dict__来实现,既然上面会造成无限递归,那我们直接用索引来修改key和value
n1 = Num(8)
print(n1.__dict__)  # {'y': 8}
n1.z = 10           # 第二次修改
print(n1.__dict__)  # {'y': 8, 'z': 10}
n1.x = 6            # 修改类的数据属性
print(n1.__dict__)  # {'y': 8, 'z': 10, 'x': 6}
print(Num.x)        # 类的数据属性本身没变
class Foo():
    def __init__(self, x):
        self.x = x

    def __getattr__(self, item):
        print('执行getattr')

    def __getattribute__(self, item):
        print('执行getattribute')
        # raise TabError('小弟发现异常')
        # raise ('hhha')
f1 = Foo(10)
f1.x      # 执行getattribute
f1.xxx    # 你访问的属性不存在,触发__getattribute__
f1.xxx    # 小弟发现异常
# getattribute是小弟,getattr为大哥,无论调用是否存在的属性都会触发系统内置的小弟,小弟原来的技能就是返回正确的属性/报错
# 当小弟和大哥同时存在时,小弟先上,此处我们重新定义了小弟,就可以自定义小弟报出错误的提示了

继承的方式完成包装

class List(list):
    def append(self, p_object):
        if type(p_object) is str:
            # self.append(p_object) 此处会进入无限循环
            # list.append(p_object) 此处调用的是list的append
            super().append(p_object)  # 调用父类方法是使用super()
        else:
            print('只能添加字符串')

    def show_middle(self):
        mid = int(len(self)/2)
        return self[mid]
L1 = List('hello_world')
l2 = list('hello_world')
print(L1,type(L1))    # <class '__main__.List'>
print(l2,type(l2))    # <class 'list'>
print(L1.show_middle())  # _ 拿到最中间的字符串了
l2.append('qwer')     # 工厂函数append在乖乖地履行着自己的义务
print(l2)             # 成功添加
L1.append(123)        # 只能添加字符串 调用的是自己类下的方法
L1.append('qwer')     # 自己类下的方法
print(L1)             # 定制的append函数添加成功
import time
class FileHandle:
    def __init__(self,filename, mode = 'r', encoding = 'utf-8'):
        self.file = open(filename, mode, encoding = encoding)
        # 定义了一个self.file的方法,方法内容是用工厂函数open,得到一个文件句柄
        self.mode = mode
        self.encoding = encoding
    def write(self,line):
        # print('运行了自己的write',line)
        t = time.strftime('%Y-%m-%d %X')    # 我们可以定制我们的write,在运用了原有的功能之外还增添了新功能
        self.file.write('%s %s' % (t, line))  # 此处的write是工厂函数的write方法
    def __getattr__(self, item):
        # print(item, type(item))
        return getattr(self.file, item)  # self.file可以拿到工厂函数open
        # item即f1.read的read,字符串类型,getattr为字符串提供找到自己的属性

f1 = FileHandle('a.txt', 'r+')
print(f1.file)  # <_io.TextIOWrapper name='a.txt' mode='w' encoding='utf-8'>
print(f1.read)  # 找不到会触发__getattr__,此时可以拿到f1的read方法了
f1.read()       # a.txt成功创建
f1.write('qwer\n')  # 成功写入'qwer'
f1.write('cpu负载过高\n')
f1.write('内存剩余不足\n')
f1.write('硬盘剩余不足\n')
f1.seek(0)
print(f1.read())

 item系列

class Foo:
    def __init__(self, item):
        self.item = item

    def __getitem__(self, item):
        print('__getitem__触发了')
        return self.__dict__[item]

    def __setitem__(self, key, value):
        print('__setitem__触发了')
        self.__dict__['key'] = value

    def __delitem__(self, key):
        print('__delitem__触发了')
        self.__dict__.pop(key)

f1 = Foo(8)
print(f1.__dict__)  # {'item': 8}
f1.item = 9
print(f1.__dict__)  # {'item': 9} 修改成功,但没有触发setitem
f1['item'] = 10     # 这样也修改成功,并且触发了setitem
f1.name = 'harvey'  #  ---->setattr-------->f1.__dict__['name']='egon'
print(f1.__dict__)  # {'item': 9, 'key': 10, 'name': 'harvey'} 添加成功
f1.__dict__['age'] = 18  # --->setitem--------->f1.__dict__['name']='egon'
print(f1.__dict__)  # {'item': 9, 'key': 10, 'name': 'harvey', 'age': 18} 添加成功
# 用.调用并不会触发setitem,而触发setattr,用__dict__索引修改才会触发setitem
# del f1.name             # 删除也是一样
# del f1.__dict__['age']
# print(f1.__dict__)
f1['age']           # __getitem__触发了
print(f1['age'])    # 18 查询成功

自定义对象的字符串显示形式(__str__,__repr__)

# l = list('hello')
# print(l)     # ['h', 'e', 'l', 'l', 'o']
# file = open('test.txt','w')
# print(file)  # <_io.TextIOWrapper name='test.txt' mode='w' encoding='cp936'>
# # 每个对象都有自己默认的字符串显示形式,通过以下方法可以自定义其显示方法
class Foo:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return '名叫%s,年龄%s' %(self.name, self.age)
f1 = Foo('harvey', 'age')
print(f1)   # 名叫harvey,年龄age
# 原理是当执行print()时,会触发str(),str(f1)-->f1.__str__,当我们覆盖了系统内置的__str__s时,就可以实现了

class Foo:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return '这是str'

    def __repr__(self):
        return '名叫%s,年龄%s' %(self.name, self.age)
    # repr作用和原理与str相同,在解释器中默认触发的是repr,在此两者都存在时,会优先触发str
    # 如果找不到str,repr会完成str的效果,心甘情愿充当替身

f1 = Foo('harvey', 'age')
print(f1)   # 这是str

省内存之__slots__

# 省内存方法 __slots__
class Foo:
    __slots__ = ['name', 'age']  #  ['name':None,'age':None]

f1 = Foo()
f1.name = 'harvey'
# print(f1.__dict__) 报错,在slots方法下,将不再产生dict
print(f1.__slots__)   # ['name', 'age'],只能通过slots查看
print(Foo.__slots__)  # ['name', 'age']
# f1.gender = '18'    # 不能再自定义属性,只能创建或修改已经创建好的key对应的value
f1.name = 'egon'      # 只能修改已有的name和age的value,不能再创建
print(f1.name)        # egon 修改成功
f1.age = '28'
print(f1.age)         # 28
# 再实例化一个也同样
f2 = Foo()
print(f2.__slots__)
f2.name = 'alex'
f2.age = 18
print(f2.name)
print(f2.age)
# 注意,此前的getattr,itemattr等方法都是建立在__dict__之上的,用了此方法虽然省内存,但之前的方法都将无法使用

__doc__

class Foo:
    '我是描述信息'
    pass
print(Foo.__doc__)   # 我是描述信息

class Bar(Foo):
    pass
# print(Bar.__doc__) # 该属性无法继承给子类

# print(Bar.__doc__) # 该属性无法继承给子类

index

# 可以查看从远方调用来的兄弟是来自哪一个模块和哪一个类的
from lib.aa import C


c1=C()
print(c1.name)

print(c1.__module__)
print(c1.__class__)

__call__

# 实例()是执行所在类的__call__方法,而类本身也是对象,类()实际也在执行类所在的一个大类的__call__
class Foo:
    def __call__(self, *args, **kwargs):
        print('实例执行啦 obj()')

f1=Foo()

f1() #f1的类Foo 下的__call__

Foo() #Foo的类 xxx下的__call__

 析构

自动回收内存 

class Foo:
    def __init__(self,name):
        self.name=name
    def __del__(self):
        print('我执行啦')

f1=Foo('alex')

# del f1    #删除实例会触发__del__
del f1.name #删除实例的属性不会触发__del__
print('--------------------->')

#程序运行完毕会自动回收内存,触发__del__

迭代器

class Foo:
    def __init__(self,n):
        self.n = n

    def __iter__(self):
        return self

    def __next__(self):
        if self.n == 15:
            raise ('迭代中止')
        self.n += 1
        return self.n
f1 = Foo(10)
print(f1.__next__())    # 11
print(f1.__next__())    # 12
print(f1.__next__())    # 13
print(f1.__next__())    # 14
# 迭代器会触发iter和next方法,使类中的实例迭代,就必须创建逻辑
# for循环能帮我们自动迭代并寻找到中止命令
for i in f1:     # obj=iter(f1)------------>f1.__iter__()
    print(i)     # obj.__next_()
# 斐波那契数列
class Fib:
    def __init__(self):
        self._a = 1
        self._b = 1

    def __iter__(self):
        return self

    def __next__(self):
        if self._a > 100:
            raise StopIteration('就到这停止')            # 这样停止是不会抛出错误的
        self._a, self._b = self._b, self._a + self._b
        return self._a
f1 = Fib()
print(next(f1))
print(next(f1))
print(next(f1))
print('我累了,歇会儿待会儿继续')   # 中间暂停的话也是从上一个迭代对象开始继续迭代,不会从头开始
print(next(f1))
print(next(f1))

for i in f1:
    print(i)

数据描述符

分为两种:

1、数据描述符————至少实现了__get__和__set__这两个方法

2、非数据描述符————没有实现__set__

class Foo:
    def __get__(self, instance, owner):
        print('get方法触发了')

    def __set__(self, instance, value):     # instance就是传过来的实例
        print('set方法触发了', instance, '修改为%s' % value)
        instance.__dict__['x'] = value  # b1.__dict__,这样就可以完成修改操作和打印出属性字典了

    def __delete__(self, instance):
        print('delete方法触发了')


class Bar:
    x = Foo()       # 将一个类赋值给另一个类的实例属性
    def __init__(self,n):
        self.x = n  # b1.x = 10
b1 = Bar(10)    # set方法触发了
# print(b1.__dict__)  # {}
b1.x = 123      # set方法触发了
print(b1.__dict__)    # {}
# 现在处理任何关于x时,系统都将其扔给Foo类处理,触发了get但属性字典为空
b1.y = 6789           # 并没有跟x沾边,只在自己的类中操作就不会触发描述符
print(b1.__dict__)    # {'y': 6789}
print(Bar.__dict__)   # 'x': <__main__.Foo object at 0x00000298C3BB9390>
del b1.x              # 删除这个实例属性,delete方法触发了
print(b1.__dict__)
b1.x                  # get方法触发了
b1.x = 1              # set方法触发了

描述符优先级

class Foo:
    def __get__(self, instance, owner):
        print('get方法触发了')

    def __set__(self, instance, value):     # instance就是传过来的实例
        print('set方法触发了', instance, '修改为%s' % value)
        instance.__dict__['x'] = value  # b1.__dict__,这样就可以完成修改操作和打印出属性字典了

    def __delete__(self, instance):
        print('delete方法触发了')


class Bar:
    x = Foo()       # 将一个类赋值给另一个类的实例属性

    def __setattr__(self, key, value):
        self.__dict__['key'] = value
        print('setattr运行了')

# 类属性 > 数据描述符
# Bar.x = 1
# print(Bar.x)
# print(Bar.__dict__)  # 当类属性重新定义x时,会覆盖之前的Foo()

# 数据描述符 > 实例属性
# b1 = Bar()
# b1.x = 1    # set方法触发了 <__main__.Bar object at 0x0000021E94769390> 修改为1
# b1.x = 1    # setattr运行了。将x = Foo()注释之后,就会触发自己类中的修改函数

# 实例属性 > 非数据描述符
class Foo:
    def __get__(self, instance, owner):
        print('get方法触发了')
class Bar:
    x = Foo()       # 将一个类赋值给另一个类的实例属性
b1 = Bar()
b1.x = 1            # 定义到自己的属性字典中了
print(b1.__dict__)  # {'x': 1} 不会触发非数据描述符(get)

上下文管理

with obj as  f:
    '代码块'
1.with obj ----》触发obj.__enter__(),拿到返回值

2.as f----->f=返回值、

3.with obj as f  等同于     f=obj.__enter__()

4.执行代码块
一:没有异常的情况下,整个代码块运行完毕后去触发__exit__,它的三个参数都为None
二:有异常的情况下,从异常出现的位置直接触发__exit__
    a:如果__exit__的返回值为True,代表吞掉了异常
    b:如果__exit__的返回值不为True,代表吐出了异常
    c:__exit__的的运行完毕就代表了整个with语句的执行完毕

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

    def __enter__(self):
        print('运行enter')
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('执行__exit__')
        print('执行exc_type')
        print('执行exc_val')
        print('执行exc_tb')

# 先执行enter,接着执行逻辑,若无错误,全部执行结束后,执行exit,程序结束
# 相当于Foo实例化一个a.txt的类
# with Foo('a.txt') as f:     # with触发enter,f为返回值
#     print(f)                # <__main__.Foo object at 0x00000262642998D0>
#     print(f.name)           # a.txt
#     print('---')
#     print('---')
#     print('---')
#     # 执行__exit__
#     # <class 'NameError'>
#     # name 'jkl' is not defined
#     # <traceback object at 0x0000026758BACD48>
# print('qwer')   # qwer




# 先执行enter,执行逻辑到有错误的那一步停止,报错,执行exit,程序结束
with Foo('a.txt') as f:     # 运行enter with触发enter,f为返回值
    print(f)                # <__main__.Foo object at 0x00000262642998D0>
    print(jkl)              # 执行__exit__ 报错
    # <class 'NameError'>
    # name 'jkl' is not defined
    # <traceback object at 0x0000026758BACD48>
    print(f.name)           # 不执行
    print('---')            # 不执行
    print('---')            # 不执行
    print('---')            # 不执行
print('qwer')               # 不执行

def __exit__(self, exc_type, exc_val, exc_tb):
        print('执行__exit__')
        print(exc_type)
        print(exc_val)
        print(exc_tb)
        return True         # 返回值设置成True,不会报错



# 先执行enter,执行逻辑到有错误的那一步停止,但不报错,执行exit
with Foo('a.txt') as f:     # 运行enter with触发enter,f为返回值
    print(f)                # <__main__.Foo object at 0x00000262642998D0>
    print(jkl)              # 执行__exit__ 
    # <class 'NameError'>
    # name 'jkl' is not defined
    # <traceback object at 0x0000026758BACD48>
    print(f.name)           # 不执行
    print('---')            # 不执行
    print('---')            # 不执行
    print('---')            # 不执行
print('qwer')               # qwer 因为是在程序外边,且无报错程序,所以正常运行

数据描述符的应用

class People:
    def __init__(self, name, age, salary):
        self.name = name
        self.age = age
        self.salary = salary

p1 = People('harvey', 18, 98)
# 如果乱输入也会正常实例化,如果我们想限制输入的类型呢?
p2 = People('123', 'qwe', 98)
# 如果只有get方法只是非数据描述符,优先级在实例属性之后,无法在输入参数前对其进行判断
# 所以此处我们需要让它变成优先级高于实例属性的数据运算符,也就是需要定义set方法
class Type:
    def __init__(self, key):
        self.key = key

    def __get__(self, instance, owner):  # instance此处为p1,owner为p1的类
        print('get执行了')
        # print(owner)
        return instance.__dict__[self.key]

    def __set__(self, instance, value):
        print('set执行了')
        if not isinstance(value, str):
            print('你输入的%s不是字符串类型' % value)
            return
        instance.__dict__[self.key] = value


class People:
    name = Type('name')     # 用type实例化一个p1,此处相当于p1.__set__() 即self.__set__()
    def __init__(self, name, age, salary):
        self.name = name
        self.age = age
        self.salary = salary

p1 = People('harvey', 18, 98)   # set执行了
print(p1.name)                  # get执行了 harvey
p2 = People(20, 18, 98)  # set执行了 你输入的20不是字符串类型
class Type:
# 上述方法只能限定是字符串类型,那如果我们还想限制age参数是int类型呢?
    def __init__(self, key, expected_type):
        self.key = key
        self.expected_type = expected_type

    def __get__(self, instance, owner):  # instance此处为p1,owner为p1的类
        print('get执行了')
        # print(owner)
        return instance.__dict__[self.key]

    def __set__(self, instance, value):
        print('set执行了')
        if not isinstance(value, self.expected_type):
            raise TypeError('你输入的%s不是[%s]类型' % (value, self.expected_type))
            return
        instance.__dict__[self.key] = value

    def __delete__(self, instance):
        print('delete执行了')
        return instance.__dict__.pop(self.key)

class People:
    name = Type('name', str)
    age = Type('age', int)
    def __init__(self, name, age, salary):
        self.name = name
        self.age = age
        self.salary = salary

p1 = People('harvey', 18, 98)   # set执行了 set执行了(name,age 两次)
print(p1.name)                  # get执行了 harvey
p2 = People(123, 18, 98)     # set执行了 TypeError: 你输入的123不是[<class 'str'>]类型
p2 = People(123, 'jkl', 98)  # TypeError: 你输入的123不是[<class 'str'>]类型

类的装饰器

# 函数装饰器
def deco(func):
    print('......')
    return func

@deco               # test = deco(test)
def test():
    print('test函数运行')
test()
# 类的装饰器
@deco
class Foo():        # Foo = deco(Foo)
    pass
# 函数装饰器
def deco(obj):
    print('......', obj)
    obj.x = 1
    obj.y = 2
    obj.z = 3
    return obj

@deco               # Foo = deco(Foo)
class Foo:
    pass
print(Foo.__dict__)

# 一切皆对象之验证
# @deco               # test = deco(test)
# def test():
#     print('运行test')
#     test.x = 1
#     test.y = 2
# print(test.__dict__)

类的装饰器强化版

def typed(**kwargs):
    def deco(obj):
        for key, value in kwargs.items():   # 将字典组成元组,将值赋给key和value
            # obj.key = value               # 相当于新增一个名为key的属性
            # obj.__dict__[key] = value     # 相当于覆盖obj属性字典
            setattr(obj, key, value)
        return obj
    return deco                 # 只要运行typed函数,就会运行deco

# 这样程序不会被写死,也可以随时增加数据属性
@typed(x = 1, y = 2)            # typed()-->return deco-->deco(Foo)
class Foo:
    pass
print(Foo.__dict__)     # 添加成功

# 下面我们再定义一个类,加一个属性
@typed(name = 'harvey')
class Bar:
    pass
print(Bar.__dict__)     # 添加成功

类的装饰器 + 数据描述符

# 数据描述符用来限制key的type
class Type:
    def __init__(self, key, expected_type):
        self.key = key
        self.expected_type = expected_type

    def __get__(self, instance, owner):  # instance此处为p1,owner为p1的类(People)
        print('get执行了')
        # print(owner)
        return instance.__dict__[self.key]

    def __set__(self, instance, value):
        print('set执行了')
        if not isinstance(value, self.expected_type):
            raise TypeError('你输入的%s不是[%s]类型' % (value, self.expected_type))
            return
        instance.__dict__[self.key] = value

    def __delete__(self, instance):
        print('delete执行了')
        return instance.__dict__.pop(self.key)

# 装饰器用来避免重复代码,例如此处的‘name = Type('name',str)\age = Type('age',int)’
def deco(**kwargs):                      # kwargs = {'name':str, 'age':18}
    def wrapper(obj):                    # obj = People
        for key, val in kwargs.items():  # (('name',str),('age',int))
            setattr(obj, key, Type(key,val))
            # setattr(People,'name',Typed('name',str)) #People.name=Typed('name',str)
        return obj
    return wrapper

@deco(name = str, age = int)    # @wrapper-->People = wrapper(People)
class People:
    def __init__(self, name, age, salary, gender):
        self.name = name
        self.age = age
        self.salary = salary
        self.gender = gender
p1 = People('harvey', 18, 98, 'male')
print(People.__dict__)

property的定制版

# print(r1.area())    # 这样会运行,如果我们想封装起来呢
# print(r1.area)      # 这样无法得到运行结果,需要借助property
# 但系统的properpy并不能出发area的运行,要想r1.area直接触发运行,我们可以定制自己的property
class My_property:
    def __init__(self,func):
        self.func = func
# 此时的功能只是跟默认的相同,并没有实现我们期望的效果
    def __get__(self, instance, value):
        print('get运行了')
        print(instance)       # <__main__.Room object at 0x000001B96027E240> 即r1
        print(value)          # <class '__main__.Room'>   即r1所在的类
        if instance is None:  # 此处目的在于如果Room.area的话,instance会缺失,为避免报错,我们直接返回My_property的产生的实例
            return self
        res = self.func(instance)
        return res
class Room:
    def __init__(self,name,width,length):
        self.name = name
        self.width = width
        self.length = length
    @My_property    # area = My_property(area)
    def area(self):
        return self.width * self.length
r1 = Room('厕所',1,1)
print(r1.area)      # 1,完美!

最终版

# 如果要多次使用r1.area,每次都得运行这么一堆代码才可以得到的话,太浪费内存
# 所以新的需求来了,把第一次运算的结果存下来,以后再使用就可以直接拿了
class My_property:
    def __init__(self,func):
        self.func = func
# 此时的功能只是跟默认的相同,并没有实现我们期望的效果
    def __get__(self, instance, value):
        # print('get运行了')
        # print(instance)     # <__main__.Room object at 0x000001B96027E240> 即r1
        # print(value)        # <class '__main__.Room'>   即r1所在的类
        if instance is None:
            return self
        res = self.func(instance)
        setattr(instance,self.func.__name__,res)    #r1,area,area(r1)
        # 此步骤是在操作instance的属性字典,key为area,即函数名,value为area的计算结果
        return res
class Room:
    def __init__(self,name,width,length):
        self.name = name
        self.width = width
        self.length = length
    @My_property    # area = My_property(area)
    def area(self):
        return self.width * self.length
r1 = Room('厕所',2,2)
print(r1.area)      # 1,完美!
print(r1.__dict__)  # {'name': '厕所', 'width': 1, 'length': 1, 'area': 1}

property干货!!!

# 第一种方法
class Foo:
    @property
    def get_AAA(self):
        print('这样get才能运行')

    # 3、需要添加此步才可以进行修改和设置
    @AAA.setter
    def set_AAA(self,value):
        print('这样set才可以运行')

    # 6、
    @AAA.deleter
    def del_AAA(self):
        print('这样del才可以运行')

f1 = Foo()
f1.AAA      # 1、此时可以正常调用属性
# f1.AAA = 1  2、但设置或修改缺会报错
f1.AAA = 1  # 4、现在就不会报错了
del f1.AAA  # 5、删除也是一样
# 注意,它们的顺序在def时不可以颠倒
# 只有在AAA属性进行@property之后才可以用AAA.setter,AAA.deleter


# 第二种方法
class Foo:
    def get_AAA(self):
        print('这样get才能运行')

    def set_AAA(self,value):
        print('这样set才可以运行')

    def del_AAA(self):
        print('这样del才可以运行')
    AAA = property(get_AAA,set_AAA,del_AAA) #此处的顺序还是不可以颠倒

f1 = Foo()
f1.AAA      
f1.AAA = 1  
del f1.AAA

元类

Python所谓一切皆对象,既然是对象就会有他的所属类,通常我们所创建的类,它们都属于元类,即Type类。

class Foo:
    pass
f1 = Foo()  # f1是通过Foo类实例化的对象
print(type(f1))     # <class '__main__.Foo'>
print(type(Foo))    # <class 'type'>

class Bar:
    pass
print(type(Bar))    # <class 'type'>
# 通常的方法创建类
class Foo:
    x = 1
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def test(self):
        print('=====>')
print(Foo)
print(Foo.__dict__)



# 通过元类实例化出一个类
def __init__(self,name,age):
    self.name=name
    self.age=age
def test(self):
    print('=====>')
Bar = type('Bar',(object,),{'x':1 , '__init__':__init__,'test':test})

b1 = Bar('harvey',18)
print(Bar)
print(Bar.__dict__)
b1.test()

自定制一个类

class Mytype(type):
    def __init__(self,a,b,c):
        print('构造函数的运行')
    def __call__(self, *args, **kwargs):
        obj = object.__new__(self) # f1 = object.__new__(Foo),创造出一个没有任何属性的实例
        self.__init__(obj, *args, **kwargs) # Foo.__init__(f1,'harvey')将实例的属性封装给obj
        return obj              # 将封装完毕的f1返回给调用他的地方
class Foo(metaclass=Mytype):    # Foo = Mytype(Foo,'Foo',(),{})即实例化的过程,会出发My的__init__
    def __init__(self,name):
        self.name = name        # f1.name = name
f1 = Foo('harvey')              # 会触发__call__
f1.name
print(f1.__dict__)

异常处理

用if语句也可以实现异常处理,但可读性差并且代码重复太多

def test():
    print('test')

choice_dic = {'1' : test()}

while True:
    choice = input('请输入:').strip()
    if not choice or choice not in choice_dic:
        continue                # 这就是一种异常处理的方式
    choice_dic[choice]

 又或者是

age = input('--> ')
if age.isdigit():
    int(age)

elif age.isspace():
    print('用户输入的是空格')

elif len(age) == 0:
    print('不可输入为空')

else:
    print('其他非法输入')
##########################################
num = input('--> ')
if num.isdigit():
    int(age)

elif num.isspace():
    print('用户输入的是空格')

elif len(num) == 0:
    print('不可输入为空')

else:
    print('其他非法输入')

 # 如果两个属性的判断条件相同,那将会有太多重复代码。
 # 你可能会说,那我这样不就行了?

if age.isdigit() and num.isdigit():
    int(age)
    int(num)
# 那如果有age1age2age3呢?那岂不是要很多的'and'?

所以,最合理的异常处理方法来了!

try:                    # 首先是两段判断条件相同的逻辑
    age = input('-->')
    int(age)

    num = input('-->')
    int(num)
except ValueError as V: # 可以简化异常名称,也可以不用as
    print(V)
# 需要注意的是,if语句是先捕捉异常,而try是运行之后再捕捉异常

异常也属于一个类,即异常类,例如ValueError是Value类的异常,也只能处理Value的异常,IndexError只能处理索引的异常,遇到处理不了的异常或者说超出我们定义的异常范围,同样会报错。总有想不到的异常,但我们有万能方法,相当于if中的else。

try:
    age = input('输入age--> ')
    int(age)

    num = input('输入num--> ')
    int(num)

    l = [2]
    l[3]

    dic = {}
    dic['name']

except ValueError as V:     # 此处的多个except就相当于elif
    print(V)
except IndexError as V:
    print(V)
except KeyError as V:
    print(V)
# 万能方法
except Exception as V:
    print(V)
print('不影响我')

当然我们会有疑问那既然有了万能的方法,还要记住别的类异常干嘛?那我们可以分两种情况:

1、如果我们的理想效果是无论有什么异常,只要有异常了,都统一执行Exception以下的代码,这个时候适合用我们万能的方法,大胆的去做吧。

2、如果我们期望的效果是碰到不同的异常,我们有不同的执行逻辑去等着它们,那我么需要将异常分类处理。

当然,当我们把需要特殊处理的异常都分别区分之后,也可以再用Exception

异常的其他机构(else,finally)

不要混淆与if中的else,完全不同的概念

s1 = 'harvey'
try:
    int(s1)

except ValueError as V:
    print(V)

except IndexError as V:
    print(V)

except KeyError as V:
    print(V)

except Exception as V:
    print(V)

else:
    print('如果没有触发异常,会触发我')

finally:
    print('无论异常与否都会触发我,通常做清理工作')
print('不影响我')

自定义异常

class My_exception(BaseException):
    def __init__(self,msg):
        self.msg = msg

raise My_exception ('自己的异常')    # __main__.My_exception: 自己的异常

断言

def test():
    # 此处有一万行代码
    # ...
    # ...
    x = 1
    return x
res = test()

assert res == 2     # AssertionError
# 作用相当于:
# if res != 1:
#     raise AssertionError

# 此处有五万行代码全指望着res == 1的时候他们才能运行

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值