美文网首页
面向对象 五

面向对象 五

作者: 吃可爱长大鸭 | 来源:发表于2021-03-04 18:53 被阅读0次

目录

1.isinstance和isinstance
2.反射
  2.1 反射模块
  2.2 反射实际应用案列
  2.3 反射隐藏属性and设置属性
3.反射内置方法 __setattr__  __delattr__  __getattr__
4.__getattribute__ 属性被访问时行为
5.__setitem__ __getitem__ __delitem__  []获取 设置 删除
6.__format__格式化打印格式
7.__str__ 显示内容
8.__del__ 关闭系统资源
9.__doc__ 类的注释信息
10.__call__ 预执行
11.__new__ 对象生成
12.__init__ 初始化
13.__all__ 限制属性
14.__len__ 计算长度
15.__hash__ 转换类型
16.__eq__ 对象 == 触发
17. __class__ 显示对象类
18.__module__ 显示对象所属模块
19.__slots__ 控制对象可以使用的属性
20. __dict__ 对象属性
21.__next__和__iter__ 实现迭代器
22.__enter__和__exit__ 上下文管理器
23.描述符
    23.1 描述符小案例
    23.2 描述符的分类
    23.3描述符机制来实现类型限制功能
24.类的装饰器
     24.1 自制property装饰器
     24.2 @classmethod类方法
     24.3 @staticmethod 静态方法

1.isinstance和isinstance

# 判断某个对象是不是某个类的实例
# isinstance() 
class Person:
    pass
class Student(Person):
    pass

stu = Student()

#判断 两个对象是不是同一个类型
print(type(1) == type(1))

# 判断stu对象是不是Student类的实例
print(isinstance(stu,Student))

# 是不是子类
# issubclass()  

# 判断一个类是不是 另一个类子类 (所有类都是object的子类或子子类)
print(issubclass(Student,object))


# 单继承(父类--》父类)
class Animal:
    pass


class Dog(Animal):
    pass

class TDog(Dog):
    pass

print(issubclass(TDog,Dog))
print(issubclass(TDog,Animal))



# 多继承下
import json

class Foo:
    pass
# 多继承
class Mydic(dict,Foo):
     def to_json(self):
        return json.dumps(self)

#  判断多继承类是不是子类
print(issubclass(Mydic,dict))
print(issubclass(Mydic,Foo))


# py3中:所有的类都是由type构造来的
# py3中:所有类都继承object
# print(type(object))
# print(type(type))

2.反射

"""
  反射:通过字符串来操作类或者对象的属性
      hasattr()  # 判断一个对象中是否由某个属性或方法
      getattr()  # 通过字符串获取对象中的属性或方法
      setattr()  # 通过字符串设置对象的属性或方法
      delattr()  # 通过字符串删除对象的属性或方法

"""
#  一、hasattr()  # 判断一个对象中是否由某个属性或方法

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

    def print_name(self):
        print(self.name)


p = Person('mzk', 18)

# 判断p对象中是否有name属性和print_name方法

# p是对象判断字符串name属性是否存在
print(hasattr(p,'name'))
# print('name' in p.__dict__)

# p是对象判断字符串print_name方法是否存在
print(hasattr(p,'print_name'))
# print('print_name' in Person.__dict__)



# 二、getattr()  # 通过字符串获取对象中的属性或方法

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

    def print_name(self):
        print(self.name)


p = Person('mzk', 18)

# 一个通过字符串获得(反射),一个通过点:正常的对象调用方法
f=getattr(p,'print_name')
f=p.print_name
print(f)  # 函数内存地址
f() # 执行


# 三、setattr()  # 通过字符串设置对象的属性或方法

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

    def print_name(self):
        print(self.name)


p = Person('mzk', 18)

# 反射属性到对象名称空间
p=setattr(p,'sex','男')
# 对象属性的设置
p.sex='男'  
print(p)

# 反射属性到类名称空间
setattr(Person,'sex','男')
# 类的属性设置
Person.sex='未知'
print(p.sex)

# 反射方法到类的内部
def xxx(self):
    print('xxx')
    print(self.age)

# 绑定给对象的方法是写在类中,如果使用反射,要反射到类内部
setattr(Person,'print_age',xxx)
p.print_age()

# 反射对象 
def xxx(obj):
    print(obj.age)

setattr(p,'print_age',xxx)

p.print_age(p)

# 四、delattr()  # 通过字符串删除对象的属性或方法

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

    def print_name(self):
        print(self.name)


p = Person('mzk', 18)

delattr(p,'name')  # 通过反射删除了属性
print(p.name) # 查询没有

delattr(p,'print_name')  # 通过反射删除了方法
p.print_name() # 查询没有

2.1 反射模块

方法: __import__('字符串')

# 使用小方法
i=input('请输入使用哪个模块:')
# 模块以字符串形式传入
s1=__import__(i)
# 调用模块方法
s1.test()
# 打印模块中的变量
print(s1.name)

# 拓展,带路径的模块导入,点路径
i = input('请输入使用哪个模块:')
s1 = __import__(i, fromlist=True)
s1.test()
print(s1.name)

# 反射模块(py3中用的比较多,框架中)
import importlib
i=input('请输入您要使用的模块:')
a=importlib.import_module(i)
a.test()

2.2 反射实际应用案列

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

    def print_age(self):
        print(self.age)

    def print_name(self):
        print(self.name)

    def print_sex(self):
        print(self.sex)

    def print_info(self):
        print('我的名字是%s,年龄是%s,性别是%s' % (self.name, self.age, self.sex))

    def server_forever(self):
        while True:
            ss = input('请输入您要执行的方法,按q退出')  #
            if 'q' == ss.lower(): break
            if hasattr(self, ss):
                # f=getattr(self,ss)
                # f()
                # 下面这一步等同于上面注释的那两个
                getattr(self, ss)()
            else:
                print('您输入的指令不执行,我正在学习中')


p = Person('mzk', 18, '男')

p.server_forever()

2.3 反射隐藏属性and设置属性

# 反射成隐藏属性
class Person:
    def __init__(self, name, age, sex):
        self.age = age
        self.sex = sex
        self.__name = name


p = Person('lqz', 18, '男')

# name=getattr(p,'__name')  # 这个方法拿不到隐藏属性

# __dict__方法中的写法可以拿到
name=getattr(p,'_Person__name')
print(name)

# 设置成隐藏属性
class Person:
    def __init__(self, name, age, sex):
        self.age = age
        self.sex = sex
        self.__name = name

    def print_wife(self):
        print(self.__wife)


p = Person('lqz', 18, '男')

# 对象方法:设置隐藏属性
setattr(p,'__wife','刘亦菲')
print(p.__wife)  # 能拿到属性

# 对象和字符串类名加属性隐藏 
setattr(p,'_Person__wife','刘亦菲')
print(p.__dict__)
print(p.__wife) # 拿不到  

# 直接从方法中拿到返回值的隐藏属性
p.print_wife()

# 类的方法:设置隐藏属性
# 类和字符串类名加属性隐藏 
setattr(Person,'_Person__wife','刘亦菲')
print(Person.__dict__)
print(Person._Person__wife) # 拿到隐藏属性

# 直接从方法中拿到返回值的隐藏属性
p.print_wife() 

3.反射内置方法 __setattr__ adn __delattr__ and __getattr__

'''
# 面向对象中的魔法方法,在某种情况下就会触发它的执行

# __setattr__  :在  对象.属性=值  会触发它的执行
# __getattr__  :在  对象.属性   获取值会触发它的执行
# __delattr__  :在 del 对象.属性  会触发它的执行

setattr(对象,key,value)
内部:对象.__setattr__(key,value)
保证对象必须要有__setattr__,所有类的基类,object中有__setattr__
'''

class Person:
    def __setattr__(self, key, value):
        # print('我执行了')
        # print(key)
        # print(value)
       
        # setattr(self,key,value)  # 本质会调用对象自己的__setattr__ ,出递归,报错
        object.__setattr__(self,key,value)
        

    def __getattr__(self, item):  # 当属性不在对象中,会触发它的执行
        print('我执行了')

    def __delattr__(self, item):
        # delattr(self,item)
        print('删除会触发我')


p = Person()
p.name = 'mzk'

# print(p.age)

del p.name

4.__getattribute__ (了解即可)

'''
无论属性在对象中是否存在,都先触发__getattribute__的执行(用的比较少)

对象.属性 调用顺序 
      先执行:__getattribute__----》类的名称空间----》__getattr__(本质是去对象自己的名称空间拿东西)

对象.属性的查找顺序
      当getattribute与getattr同时存在,只会执行getattrbute,
      除非getattribute在执行过程中抛出异常AttributeError

重点: __getattribute__:  对象.属性---》__getattribute__---》__getattr__
'''

class Person:
    name = 'lqz'
    # 属性不存在,才触发
    def __getattr__(self, item):
        print('getattr触发了')

    def __getattribute__(self, item):
        print('__getattribute__触发了')


p = Person()
print(p.name)

5. __setitem__ 、__getitem__ 、__delitem__(了解即可)

'''
__getitem__:通过中括号取值触发它的执行
__setitem__:通过中括号赋值,触发它的执行
__delitem__:通过中括号删除值,触发它的执行

item系列
    __getitem__: 对象['name'],触发它的执行
    __setitem__: 对象['name']='value',触发它的执行
    __delitem__: del 对象['name'],触发它的执行

    应用:把一个对象做成既能.取值赋值,又能[] 取值赋值
    -自己定义这种类(会出现递归现象,反射setattr)
        -对象.__dict__
        -object.__setattr__(self,key,value) 普通函数,有几个值,就传几个值
        -object.__setattr__(self,key,value)
        -对象.__name='mzk'

    后期封装+setattr的使用(注意)
      后期源码中常出现的操作(flask框架源码中使用)
        -object.__setattr__(self,'_类名__属性名',value)
        -对象.__dict__[_类名__属性名]='mzk'
        -类内部使用  self.__属性名
        -类外部使用: 对象._类名__属性名
'''


class Person:
    def __getitem__(self, item):
        print('__getitem__执行')

    def __setitem__(self, key, value):
        print('__setitem__执行')

    def __delitem__(self, key):
        print('__delitem__执行')

p=Person()
p['name']='lqz'
p['name']
del p['age']


# 自己定义一个字典,但是不继承字典
# 作业:写一个字典,不继承字典,支持 [] 取值赋值,不支持 . 取值赋值
class Mydict:
    def __init__(self,**kwargs):
        # 方案一
        # for key,value in kwargs.items():
        #     setattr(self,key,value)

        # 方案二
        self.__dict__.update(kwargs)

        # 方案三
        # for key,value in kwargs.items():
        #     self.__dict__[key]=value

    def __getitem__(self, item):
        # 方案一
        # return self.__dict__[item]

        # 方案二
        return getattr(self,item)

    def __setitem__(self, key, value):
        # 方案一
        # setattr(self,key,value)

        # 方案二
        self.__dict__[key]=value

    def __delitem__(self, key):
        del self.__dict__[key]




dic=Mydict(name='lqz',age=18)

# 打印属性
print(dic.name)

# 触发__getitem__方法
print(dic['name'])

# 触发__setitem__方法
dic['age']=18

print(dic.age)

# 触发__delitem__方法
del dic.age

print(dic.__dict__)

6.__format__ 格式化打印格式(了解即可)

'''
__format__
    -format(对象)  ---》触发类中__format__的执行
    -'{:n-m}'.format(对象) ---》触发类中__format__的执行
'''

# 一 字符串格式
s = '我的名字是:%s' % 'mzk'
print(s)

# 字符串的format方法支持的格式化方式

res = '我的名字是{},年龄是{}'.format('mzk', 18)
res = '我的名字是{0},年龄是{1}'.format('mzk', 18)

ll = ['mzk', 19]
l2 = ['男']
res = '我的名字是{0[0]},年龄是{0[1]},性别是{1[0]}'.format(ll, l2)
dic = {'name': 'mzk', 'age': 19, 'sex': '男'}
res = '我的名字是{name},年龄是{age},性别是{sex}'.format(**dic)


class Person:
    def __init__(self):
        self.name = 'mzk'
        self.age = 19
        self.sex = '男'


p = Person()
# res='我的名字是{0.name},年龄是{0.age},性别是{0.sex}'.format(p)
res = '我的名字是{obj.name},年龄是{obj.age},性别是{obj.sex}'.format(obj=p)
print(res)


# 对象的__format__

class Person():
    __dic = {
        'n-a': '名字是:{obj.name}-----年龄是:{obj.age}',  # 名字是:mzk-年龄是:18
        'n:a': '名字是:{obj.name}:::::年龄是:{obj.age}',  # 名字是:mzk:年龄是:18
        'n/a': '名字是:{obj.name}/////年龄是:{obj.age}',  # 名字是:mzk/年龄是:18
    }

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

    def __format__(self, format_spec):
        # print(format_spec)
        if format_spec and format_spec in self.__dic:
            s = self.__dic[format_spec]
        else:
            s = self.__dic['n-a']
        return s.format(obj=self)


p = Person('mzk', 18, '男')

# 内置函数
# 第一种触发它执行
# res=format(p)
# res=format(p,'n-a')
# res = format(p,'n/a')
# res = format(p,'n:a')
print(format(p, 'n-a'))

# 第二种触发方式
# print('n-a'.format(p))


# 写一个Date类,实例化的时候传入年  月   日,格式化方法
# d=Date(2020,12,23)
# print('y-m-d'.format(p)) # 2020-12-23
# print('y/m/d'.format(p)) # 2020/12/23
# print('y:m:d'.format(p)) # 2020:12:23
# print('y年m月d日'.format(p)) # 2020年12月23日

7.__str__显示内容

"""

    __str__
    前后带杠杠的都是特殊的内置函数  会在某些时机自动执行  一般情况我们不应该直接调用他们

    当我们需要自定义打印显示内容时 就需要实现__str__方法
    该方法必须返回一个字符串  返回的是什么 打印出来就是什么

"""
class Test:
    def __init__(self,name):
        self.name = name
    def __str__(self):
        print("str run....")
        return self.name
t = Test("安米")

#  __str__ : print(对象) 触发对象的 __str__
 print(t)

# 在讲一个对象转换字符串时  本质就是在调用这个对象 __str__方法
print(str(t))

8.__del__关闭系统资源

"""
    __del__

    当对象被从内存中删除时会自动执行
    另一种情况时  程序员手动删除了这个对象 也会自动执行

    什么时候使用它
    在python中 有自动内存管理机制 所以 python自己创建的数据 不需要我们做任何操作
    但是有一种情况 我们使用python打开了一个不属于python管理的数据
    比如打开了一个文件  这个文件一定是操作系统在打开 会占用系统内存  而python解释器无法操作系统内存的
    所以 当你的python解释器运行结束后  文件依然处于打开状态  这时候就需要使用__del__来关闭系统资源

    简单地说 当程序运行结束时 需要做一些清理操作 就使用__del__

    __del__也称之为 析构函数

    分析构造 并拆除这个对象
"""


class TextFile:

    def __init__(self,filepath,mode="rt",encoding="utf-8"):
        self.file = open(filepath,mode=mode,encoding=encoding)

    def read(self):
        return self.file.read()

    def write(self,text):
        self.file.write(text)

    # 该方法其实就是一个通知性质 仅仅是告诉程序员 对象即将被删除
    def __del__(self):
        # 在这里关闭系统的文件 妥妥的
        self.file.close()


tf = TextFile("今日内容.txt")

tf.read()
tf.write()

# tf.file.close() 不需要手动关闭了  在对象删除时会自动关闭

9. __doc__ 类的注释信息

'''
 __doc__: 类下面的注释  类名.__doc__ ,把注释打印出来
'''


class Person:
    '''
    类的提示信息,注释
    '''
     pass


print(Person.__doc__)


from bs4 import BeautifulSoup

print(BeautifulSoup.__doc__)


# 注意:继承关系下,类的注释不能继承

class Animal:
    '''
    Animal的注释,动物类,所有属于动物的都继承我
    '''

class Person(Animal):
    pass


print(Animal.__doc__)
print(Person.__doc__)

10.__call__预执行

"""
__call__  调用的意思
在对象被调用时 执行 函数 类

自定义元类的目的
1.可以通过__call__ 来控制对象的创建过程
2.可用控制类的创建过程
"""

# 自定义一个元类 元类也是一个类   但是需要继承type
class MyMeta(type):

    # self 表示要创建对象的那个类(Person)  *args是调用Person类时传入的参数
    def __call__(self, *args, **kwargs):
        print("MyMte中的 call run'")
        print(self, *args, **kwargs)

        # 下面的三步是固定写法 一个模板 只要你需要控制对象的创建过程 就应该先把模板写出来

        # 1.创建空对象
        obj = object.__new__(self)
        # 2.调用初始化方法
        self.__init__(obj, *args, **kwargs)
        # self.__init__(obj)
        # 3.得到一个完整的对象
        return obj


# 修改Person类的元类为MyMeta
class Person(metaclass=MyMeta):

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

    def __call__(self, *args, **kwargs):
        print("call run...")


# 调用Person这个对象时 执行的是 Person的类(type)中__call__ 方法
p = Person("张三疯", 80)

# print(p)
# 当调用对象时 会执行该对象所属类中的__call__方法
p()

# print(p.name)
# print(p.age)

11.__new__ 对象生成

'''
__new__   对象生成 ,类() 会触发,是在__init__之前触发
类实例化得到对象的本质是先调用__new__生成一个空对象,然后再调用__init__完成初始化
总结:类实例化得到对象,本质是在执行类的 __new__,返回什么对象,p拿到的就是什么对象,在__new__内部,调用了__init__


'''
class Person:
    def __init__(self, name):
        # 初始化,self这个对象,已经造出来了
        # self是在哪里造出来的? __new__中造出来的
        print('__init__执行了')
        print(self)
        self.name = name

    # def __new__(cls, *args, **kwargs):
    #     print('我出生了')
    #     print(args)
    #     print(kwargs)
    #     # return 1
    #     cls.__init__(1,*args)
    #     ## 如果一个类,没有重写 __new__方法,在__new__中自动触发了对象的__init__的执行

    def __new__(cls, *args, **kwargs):
        print('我出生了')
        # cls(*args,**kwargs)  # 出递归

        # obj=object.__new__(cls) # 静态方法之类来调用
        obj = super().__new__(cls)  # 静态方法之对象来调用

        cls.__init__(obj, *args, **kwargs)  # 穿衣服,调用__init__
        ## 如果一个类,没有重写 __new__方法,在__new__中自动触发了对象的__init__的执行
        return obj  # 必须要return,造出来的对象


p = Person('mzk')
print(p)
print(p.name)


# 列子
'''
Person()---->本质触发的是__new__,因为__new__返回什么对象就是什么
__init__是初始化方法,在__new__后执行的

__new__:创造出一个空对象,没有初始化
__init__:创造完空对象后,再触发它,完成初始化

'''
class Person:
    def __init__(self,name):
        print('我执行了,我是__init__')
        self.name=name
    def __new__(cls, *args, **kwargs):
        print('我出生了')
        # __new__内部会触发 self.__init__的执行
        obj=object.__new__(cls)
        # return {'name':'l1z'}  # 不会触发__init__
        return obj  # 触发 __init__


p=Person('lqz')  # 触发__new__,返回 1 对象

print(p)

12.__init__ 初始化

'''
 __init__  对象初始化调用   类() 会触发
'''

13.__all__ 限制属性

# a.py
__all__=['age','name']
age=10
name='lqz'
sex='男'

# __all__:使用from  a import  *导入模块时,限制能够使用的属性
# 在使用from a import * 导入模块时,只能导入__all__指定的属性
from  a import  *

print(age)
print(name)
# print(sex)

14.__len__ 计算长度

'''
 __len__:计算对象长度,返回几,len(对象),结果就是几
len(对象)----》触发对象的__len__()
'''

class Foo:

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

    def __len__(self):
        return len(self.name)


f = Foo('lqz', 19)

print(len(f))

15.__hash__ 转换类型(了解即可)

'''
__hash__
    -字典类型---》hash类型---》key值必须可hash
    -可变类型(字典,列表)不可hash,不可变类型(数字,字符串,元组)可以hash,可以作为字典的key
    -重写自己定制的类的hash方法,使对象可以hash

__hash__   ,hash(对象)触发执行,返回什么就是什么
如果一个对象不可hash,通过重写 __hash__让它变得可hash
'''
# 普通用法
l=['1',3,54,56]
dic={'name':'lqz'}

a=10

print(hash(a))
# print(hash(dic))
print(hash(l))

# 魔法方法
class Foo:
    def __init__(self):
        self.name = 'lqz'
        self.l = ['1', 3, 4]

    def __hash__(self):
        return hash(self.name)


f = Foo()
print(hash(f))

16.__eq__对象 == 触发(了解即可)

'''
两个对象 == 比较的时候,触发 __eq__的执行,在内部,自定制比较规则即可
'''

class Foo:
    def __str__(self):
        return self.name
    def __init__(self):
        self.name='lqz'

    def __eq__(self, other):
        # print(self)
        # print(other)
        # 自己定制比较规则
        if self.name == other.name:
            return True
        else:
            return False


f1=Foo()
# f1.name='mzk'
f1.age=999

f2=Foo()
f2.age=888
print(f1==f2)
# print(f1 is f2)

17.__class__ 显示对象类(了解即可)

'''
 __class__   对象.__class   查看对象的类
'''
class Animal:
    pass
class Person(Animal):
    pass


print(Person.__bases__) # 查看所有父类

p=Person()

print(p.__class__) # 查看对象的类

18.__module__ 显示对象所属模块(了解即可)

'''
 __modules__:看对象属于哪个模块
'''
from a import C

obj = C()

class Person():
    pass

p=Person()
print(obj.__module__)
print(p.__module__)

19.__slots__ 控制对象可以使用的属性

'''
__slots__:控制对象可以使用的属性
    -控制对象的属性(__dict__没了)
    -对象很多,对象属性比价少
    -本质:对象没有__dict__,统一被类的__dict__管理起来了,在dict内部使用更紧凑的存储方式,节约内存空间
'''
slots的使用

对象只拥有name和age属性,其它放不进去
class Person:
    __slots__ = ['name', 'age']  # 对象只拥有name和age属性
    # __slots__ = 'name'

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

    def __str__(self):
        return self.name


p = Person('mzk', 18)
print(p.name)

p.sex = '男'

20. __dict__ 对象属性

'''
__dict__ 查看对象中的属性
'''

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

    def __str__(self):
        return self.name


p = Person('mzk', 18)
p.sex = '男'
print(p.__dict__)

21.__next____iter__实现迭代器

'''
0 python中的for循环,是依赖于迭代的循环,不依赖于索引
    # 1  for i in 可迭代对象
    # 2 java中 for(i=0;i++;i<10){}
    # 3 python中 for循环的本质
    for i in 可迭代对象:会调用可迭代对象的__iter__,得到一个迭代器对象,不停的调用对象.__next__,得到一个值,直到抛出异常,for循环结束

1 可迭代对象:对象有__iter__方法的,就是可迭代对象,调用__iter__,得到迭代器对象
2 迭代器对象:既有__iter__又有__next__对象,就是迭代器对象

__iter__和__next__
    -可迭代对象:对象有__iter__方法,这个对象就是一个可迭代对象
        -for循环本质就是在循环可迭代对象
        -for i in 对象(必须是可迭代对象)
        -for循环的本质:先调用对象的__iter__得到一个迭代器对象,然后调用迭代器对象的__next__ 方法,每次获得一个值(next这种获取值的方式不依赖于索           引,又叫迭代循环)
    -迭代器对象:
        -既有__iter__又有__next__的对象,就是迭代器对象
        -迭代器对象一定是可迭代对象
    -定制自己的可迭代对象
        -重写类中的__iter__和__next__
        -让它支持for循环直接循环(做出一种不依赖于索引取值的方式)
        -不停的return 值,当要停下时,抛出异常
'''
# 链式调用(跟语言无关)
f.test().test2().test3()  # java,js:中广泛应用,设计程序的模式

# 如何实现支持链式调用的对象
class Foo:
    def __init__(self,name,age,sex):
        self.name=name
        self.age=age
        self.sex=sex
    def print_name(self):
        print(self.name)
        return self

    def print_age(self):
        print(self.age)
        return self

    def print_sex(self):
        print(self.sex)
        return self

f=Foo('lqz',16,'男')
f.print_name()
f.print_sex()
f.print_age()

# 链式调用
f.print_name().print_sex().print_age()

# 1 给一个类,重写__iter__,__next__
class Foo:
    def __init__(self):
        self.__x = 1

    def __iter__(self):
        return self

    def __next__(self):
        self.__x += 1
        return self.__x


f = Foo()  # f就是一个可迭代对象,所以他就能够被for 循环

# 自定制可迭代对象(自己写的对象,可以迭代,可以被for循环)
for i in f:  # f.__iter__()----->每循环一次,调用一下 f.__next__()
    print(i)


# 2 加入异常,for循环该对象,可以停止
class Foo:
    def __init__(self,start,end):
        self.__start = start
        self.__end=end

    def __iter__(self):
        return self

    def __next__(self):
        self.__start+=1
        if self.__start>=self.__end:
            raise StopIteration('')  # 抛出一个异常,程序就结束了
        return self.__start

for i in Foo(1,20):
    print(i)


# 实现自己的range

class MyRange:
    def __init__(self, start, end,step=1):
        self.__start = start
        self.__end = end
        self.__step = step

    def __iter__(self):
        return self

    def __next__(self):
        self.__start += self.__step
        if self.__start >= self.__end:
            raise StopIteration('')  # 抛出一个异常,程序就结束了
        return self.__start


for i in MyRange(1, 20):
    print(i)


# 斐波那契数列(通过__iter__和__next__实现)
# 产生小于多少的斐波那契数列
class Fib:
    def __init__(self, count):
        self.start = 0
        self.second = 1
        self.count = count

    def __iter__(self):
        return self

    def __next__(self):
        self.start,self.second=self.second,self.start+self.second
        if self.start >= self.count:
            raise StopIteration()
        return self.start


for i in Fib(100):
    print(i)

22.__enter____exit__上下文管理器

'''

with open() as f:

with 上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明enter和exit方法

优点:
使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预
在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在exit中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处

-with:上下文管理协议
    -with 对象 as f:   # 处理异常,不被with管理后,会清理资源
        代码
        代码
        
    -学过的 open,支持上下文管理协议
    -自定制一些对象,支持with管理
        -在类中重写 __enter__和__exit__
        -with 你写的对象 as f:   
            # 触发对象的__enter__ 方法执行,做一些初始化的工作,返回一个对象,会给f
            # 写代码,可以有异常,不用咱们手动处理异常
        -脱离了with的管理,会执行对象的__exit__,处理异常,资源清理工作
'''
#  __enter__ 和 __exit__

class Open:

    def __enter__(self):
        print('enter执行了')

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

# with 对象,触发对象的 __enter__的执行
# 顶格写,脱离了with,就会执行 __exit__
with Open() as f:
    print('with上下文管理器')
    print(f)

print('with结束了')


class Open:
    def __str__(self):
        return 'f对象'
    def __enter__(self):
        print('enter执行了')
        return self

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

f=Open()
# print(f)
with  f as ff1:
    print('with上下文管理器')
    print(f)
    print(ff1) # enter的执行返回结果赋值给ff1
    print(f is ff1)

print('with结束了')

# exit的参数解释:exit()中的三个参数分别代表异常类型,异常值和追溯信息,with语句中代码块出现异常,则with后的代码都无法执行

class Open:
    def __enter__(self):
        print('enter执行了')
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('异常类型',exc_type)
        print('异常信息',exc_val)
        print('追溯信息',exc_tb)
        print('exit执行了')
        return True  # 忽略掉异常,不爆红


with Open() as f:
    f.witer()  # 没有

print('结束')


# 自定制一个open,打开文件

class Open:
    def __init__(self, path, mod='rt', encoding='utf-8'):
        self.f = open(path, mod, encoding=encoding)

    def __enter__(self):
        return self.f

    def __exit__(self, exc_type, exc_val, exc_tb):
        # 如果exc_tpye 不为空,表示with上下文内部出了错误,处理错误
        if exc_type:
            print('程序出错了,但是我处理好了,继续执行就好了')
            print(exc_val)
            return True
        # 没有错误,正常结束,资源清理工作(关闭文件)
        print('文件关闭了,放心好了')
        self.f.close()
    # def read(self):
    #     return self.f.read()


# 后期会使用with ,处理数据库链接,redis链接

with Open('a.txt', 'r', encoding='utf-8') as f:
    res = f.read()
    print(res)

23.描述符

'''
描述符是什么?  
    就是一个新式类,实现了 __get__   __set__   __delete__ 其中一个,就被称为描述符类

作用: 代理类属性
    class Person:
        name=描述符()   # 代理类属性

描述符
    -新式类,__get__  __set__  __delete__
    -代理类的属性(一旦使用描述符代理,对象的__dict__中没有这个属性了)
    -数据描述符和非数据描述符(__get__)
            1.类属性
            2.数据描述符
            3.实例属性(对象属性)
            4.非数据描述符
            5.找不到的属性触发getattr()
     -通过描述符写了一个控制对象类型的案例
     -通过类装饰器+描述符实现对象类型控制
     -自定制 property
     -自定制 classmethod
     -自定制 staticmethod
'''

23.1 描述符小案例

# 小案例,写一个描述符类(get,set,delete 参数都是固定的)
class Foo:
    def __get__(self, instance, owner):
        print('触发get')

    def __set__(self, instance, value):
        print('触发set')

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


f1 = Foo()
f1.name = 'mzk'  # 不会触发 __set__的执行



# 这种情况会触发

class Str:
    def __get__(self, instance, owner):
        print('触发get')

    def __set__(self, instance, value):
        print('触发set')

    def __delete__(self, instance):
        print('触发delete')
class People:
    age=19
    name = Str()
    def __init__(self, name):  # name被Str类代理,age被Int类代理
        self.name = name   # 触发描述符类,set的执行

p=People('lqz')
print(p.name) # 触发描述符类,get的执行,由于get返回None,所以打印出None

23.2 描述符的分类

描述符分类:两类
'''
数据描述符: 除了get以外的,还写了别的
非数据描述符: 只有get,非数据描述符
'''

# 注意:
'''
描述符本身应该定义成新式类,被代理的类也应该是新式类
必须把描述符定义成这个类的类属性,不能为定义到构造函数中
要严格遵循该优先级,优先级由高到底分别是
    1.类属性
    2.数据描述符
    3.实例属性(对象属性)
    4.非数据描述符
    5.找不到的属性触发getattr()

'''
#举例
class Person:
    def __init__(self):
        name='mzk'  # 在函数中定义了一个局部变量,用完就没了,不属于任何对象或类
        self.name='mzk'  # 属于对象,放在对象的名称空间中
        Person.name='mzk' # 属于类,放在类的名称空间中,对象也能使用的到

p=Person()
print(p.name) 
print(Person.name)



# 举例
class Str:
    def __get__(self, instance, owner):
        print('触发get')

     def __set__(self, instance, value):
         print('触发set')
    
     def __delete__(self, instance):
         print('触发delete')


class People:
    age = 19
    name = Str()

    def __init__(self, name):  # name被Str类代理
        self.name = name   # 触发描述符类,set的执行
        pass


p=People('lqz')
p.name='xxx'  # 放到对象的名称空间
print(p.name)

23.3描述符机制来实现类型限制功能

# python是强类型动态语言
'''
1 强类型:不同类型直接不允许直接运算
res=1+'sddd'  # python中不允许,类型转换后操作,js中允许,java:隐士类型转换,go语言强类型

2 动态语言(静态语言)
    -动态语言---->解释型:js,php,python,不使用显示数据类型声明,声明变量不需要指定变量类型 var a='ddd'
    -静态语言---->编译型:java,c,go,   
'''
# 这种只是一个提示作用,不会限制(公司里通常这么做)
def test(a:int,b:str)->int:
    print(a)
    print(b)
    return 18
test('1',89)

# 了解
class Typed:
    def __init__(self, name, expected_type):
        self.name = name
        self.expected_type = expected_type

    def __get__(self, instance, owner):
        # print('get--->', instance, owner)
        if instance is None:
            return self
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        if not isinstance(value, self.expected_type):
            print('类型不匹配')
            return

        instance.__dict__[self.name] = value
        print(instance.__dict__)

    def __delete__(self, instance):
        print('delete--->', instance)
        instance.__dict__.pop(self.name)


class People:
    name = Typed('name', str)
    age = Typed('age', int)
    salary = Typed('salary', float)

    def __init__(self, name, age, salary):
        self.name = name  # 触发Typed的 __set__,
        self.age = age
        self.salary = salary


# 类People中的属性  name:str   age:int   salary:float

p = People('mzk', '19', 1000.1)

p.salary = '99'
print(p.__dict__)

24.类的装饰器

 # 1.类的装饰器:无参
def decorate(cls):
    print('类的装饰器开始运行啦------>')
    return cls


@decorate  # 类的装饰器    People=decorate(People)  仍旧是 People=People
class People:
    pass


People()  # 等同于 decorate(People)()--等同于-->People()


# 2.类的装饰器:有参
def typeassert(**kwargs):
    def decorate(cls):
        print('类的装饰器开始运行啦------>', kwargs)
        return cls

    return decorate


@typeassert(name='mzk', age=19)  # 执行typeassert(name='mzk',age=19)--->decorate内层函数--->People=decorate(People)
class People:
    pass


p = People()


# 3.使用类装饰器+描述符,实现对对象类型的控制
class Typed:
    def __init__(self, name, expected_type):
        self.name = name
        self.expected_type = expected_type

    def __get__(self, instance, owner):
        print('get--->', instance, owner)
        if instance is None:
            return self
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        print('set--->', instance, value)
        if not isinstance(value, self.expected_type):

            print('类型不匹配怕')
            return
        instance.__dict__[self.name] = value

    def __delete__(self, instance):
        print('delete--->', instance)
        instance.__dict__.pop(self.name)


def typeassert(**kwargs):
    def decorate(cls):
        print('类的装饰器开始运行啦------>', kwargs)
        
        for name, expected_type in kwargs.items():
            '''
            # 通过反射,把 name,Typed的对象,放到类中
            # People.'name'=Typed('name',str)
            # People.'age'=Typed('age',int)
            # People.'salary'=Typed('salary',float)
            本质就是如下
            name = Typed('name', str)
            age = Typed('age', int)
            salary = Typed('salary', float)
            '''
            setattr(cls, name, Typed(name, expected_type))

        return cls

    return decorate

# 这种写法和下面的写法,效果,完全一致
@typeassert(
    name=str, age=int, salary=float
)  # 有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People)
class People:
    def __init__(self, name, age, salary):
        self.name = name    # 描述符类的 __set__
        self.age = age
        self.salary = salary


# 这种写法跟上面的写法,效果,完全一致
class People:
    name = Typed('name', str)
    age = Typed('age', int)
    salary = Typed('salary', float)
    def __init__(self, name, age, salary):
        self.name = name
        self.age = age
        self.salary = salary
print(People.__dict__)  # {'name':Typed的对象(属性不一样),'age':Typed的对象,'salary':Typed的对象}


# 实例化得到一个People对象
p=People('mzk',19,100.1)

p.age='15'  ## 描述符类的 __set__
p.age=15
print(p.__dict__)

24.1 自制property装饰器

class Lazyproperty:
    def __init__(self, func):
        self.func = func

    def __get__(self, instance, owner):
        # self.func是 Room类的area的内存地址,普通函数---需要传对象  area(instance)
        print('我被执行')
        return self.func(instance)  # 此时你应该明白,到底是谁在为你做自动传递self的事情


class Room:
    def __init__(self, name, width, length):
        self.name = name
        self.width = width
        self.length = length

    @Lazyproperty  # area=Lazyproperty(area)--->Lazyproperty类的__init__执行,把area放到了Lazyproperty的对象中然后返回了-->area代指Lazyproperty的对象
    def area(self):
        return self.width * self.length


r1 = Room('alex', 2, 3)
# r1.area是Lazyproperty的对象,类是描述符类---->r1.area会触发描述符类的 __get__方法
print(r1.area)  # 触发描述符类的 __get__ 的执行
print(r1.area)  # 触发描述符类的 __get__ 的执行
print(r1.area)  # 触发描述符类的 __get__ 的执行


# 想缓存一下

class Lazyproperty:
    def __init__(self, func):
        self.func = func

    def __get__(self, instance, owner):
        print('我被执行')
        res = self.func(instance)
        # instance是r1对象,向r1对象中放了一个 r1.'函数名字'=6
        instance.__dict__[self.func.__name__] = res
        # 相当于
        # instance.__dict__['area']=6
        return res

    # def __set__(self, instance, value):  # 加了它,变成数据描述符,查找顺序就不对了,不能加它


class Room:
    def __init__(self, name, width, length):
        self.name = name
        self.width = width
        self.length = length

    @Lazyproperty  # area=Lazyproperty(area)--->Lazyproperty类的__init__执行,把area放到了Lazyproperty的对象中然后返回了-->area代指Lazyproperty的对象
    def area(self):
        return self.width * self.length


r1 = Room('alex', 2, 3)
# r1.area是Lazyproperty的对象,类是描述符类---->r1.area会触发描述符类的 __get__方法
print(r1.__dict__)
print(r1.area)  # 触发描述符类的 __get__ 的执行
print(r1.__dict__)  # {'name': 'alex', 'width': 2, 'length': 3,area:6}

print(r1.area)  # 直接先从对象自身找
print(r1.area)  # 直接从对象自身找

24.2 @classmethod类方法

@classmethod
class ClassMethod:
    def __init__(self, func):
        self.func = func

    def __get__(self, instance,owner):  #类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身,
        def feedback():
            print('在这里可以加功能啊...')
            return self.func(owner)

        return feedback

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

    @ClassMethod    # get_obj=ClassMethod(get_obj)--->ClassMethod的对象
    def get_obj(cls):
        print(cls)
        print('这是类的绑定方法')


# People.get_obj   # 触发 描述符类的__get__,--->返回feedback的内存地址
People.get_obj() # 其实执行的是feedback()--->self.func--->People类的get_obj方法,self.func(类)--->get_obj(People传入了)

24.3 @staticmethod 静态方法

@staticmethod
class StaticMethod:
    def __init__(self, func):
        self.func = func

    def __get__(self, instance, owner):
        def feedback():
            print('在这里可以加功能啊...')
            return self.func()

        return feedback


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

    @StaticMethod  # get_obj=StaticMethod(get_obj)--->StaticMethod的对象
    def get_obj():
        print('这是静态方法')


# People.get_obj # --->触发__get__执行----feedback内存地址

People.get_obj()  # feedback()

# 带参数情况下
class StaticMethod:
    def __init__(self, func):
        self.func = func

    def __get__(self, instance, owner):
        def feedback(*args, **kwargs):
            print('在这里可以加功能啊...')
            return self.func(*args, **kwargs)

        return feedback


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

    @StaticMethod  # get_obj=StaticMethod(get_obj)--->StaticMethod的对象
    def get_obj(a, b):
        print(a, b)
        print('这是静态方法')


# People.get_obj # --->触发__get__执行----feedback内存地址

People.get_obj(7, 8)  # feedback()

相关文章

  • Java 工程师成神之路 | 2019正式版

    摘要:什么是面向对象 基础篇 01面向对象 → 什么是面向对象 面向对象、面向过程 面向对象的三大基本特征和五大基...

  • 第七篇 面向对象编程

    一、复习 二、time模块的补充 三、面向对象 四、初始面向对象和类 五、面向对象之间的交互 六、面向对象的组合 ...

  • 阿里P8资深架构师耗时一年整理19年Java工程师成神之路

    1、基础篇 01:面向对象 → 什么是面向对象 面向对象、面向过程 面向对象的三大基本特征和五大基本原则 → 平台...

  • Java 工程师成神之路 | 2019正式版

    一.基础篇 01 面向对象 → 什么是面向对象 面向对象、面向过程 面向对象的三大基本特征和五大基本原则 → 平台...

  • JavaScript之面向对象编程

    五、面向对象编程 目录:面向对象原型继承、面向对象class继承(ES6引入的) 1.面向对象原型继承 类:模板 ...

  • js面向对象

    js面向对象(第五周) 什么是面向对象 面向对象是一种编程思想,是在面向过程编程(结构化编程)之后出现的,面向对象...

  • Java 工程师成神之路

    一、基础篇 01.面向对象 → 什么是面向对象 面向对象、面向过程 面向对象的三大基本特征和五大基本原则 → 平台...

  • Java 工程师成神之路 | 2019正式版

    1 基础篇 01面向对象 → 什么是面向对象面向对象、面向过程 面向对象的三大基本特征和五大基本原则 → 平台无关...

  • Java从入门到入坑(基础篇)

    01:面向对象 1:什么是面向对象 面向对象,面向过程 面向对象的三大基本特征和五大基本原则 三大特性:封装,继承...

  • 面向对象编程

    面向对象编程 一、面向对象和面向过程 二、类和对象 三、内存图 四、构造方法 五、关键字 六、面向对象的三大特征 ...

网友评论

      本文标题:面向对象 五

      本文链接:https://www.haomeiwen.com/subject/njwkqltx.html