饮冰三年-人工智能-Python-18Python面向对象

1 类与实例对方法和属性的修改

class Chinese:
    # 这是一个Chinese的类
    #定义一个类属性,又称为静态数据或者静态变量,相当于C#中的static
    country="China"
    # 定义一个初始化函数,
    def __init__(self,name,age,gender):
        self.name=name
        self.age=age
        self.gender=gender
    #定义一个方法
    def play(self,ballName):
        print('%s 正在打 %s' %(self.name,ballName))

#1类属性
print("----------开始修改类属性")
#1.1 查看类属性
print(Chinese.country) #China
#1.2 修改类属性
Chinese.country="中华人民共和国"
print(Chinese.country) #中华人民共和国
#1.3 删除类属性
del Chinese.country
print(Chinese.__dict__)
#{'__module__': '__main__', '__init__': <function Chinese.__init__ at 0x0000000001D30EA0>, 'play': <function Chinese.play at 0x000000000247C7B8>, '__dict__': <attribute '__dict__' of 'Chinese' objects>, '__weakref__': <attribute '__weakref__' of 'Chinese' objects>, '__doc__': None}
#1.4 添加类属性
Chinese.country="中国"
print(Chinese.__dict__)
#{'__module__': '__main__', '__init__': <function Chinese.__init__ at 0x0000000002300EA0>, 'play': <function Chinese.play at 0x000000000250C7B8>, '__dict__': <attribute '__dict__' of 'Chinese' objects>, '__weakref__': <attribute '__weakref__' of 'Chinese' objects>, '__doc__': None, 'country': '中国'}

#2方法属性
print("----------开始修改方法属性")
#2.1 查看方法属性
p1=Chinese('张三',18,"")
Chinese.play(p1,'篮球') #张三 正在打 篮球
#2.2 修改方法属性
def play(self,ballName,address):
    print('%s 在%s打%s' %(self.name,address,ballName))
Chinese.play=play
Chinese.play(p1,'篮球','操场') #张三 在操场打篮球
#2.3 删除类属性
del Chinese.play
print(Chinese.__dict__)
#{'__module__': '__main__', '__init__': <function Chinese.__init__ at 0x0000000001D40EA0>, '__dict__': <attribute '__dict__' of 'Chinese' objects>, '__weakref__': <attribute '__weakref__' of 'Chinese' objects>, '__doc__': None, 'country': '中国'}
#2.4 添加方法
def eat(self,food):
    print("%s 正在吃 %s" %(self.name,food))
Chinese.eat=eat
print(Chinese.__dict__)
# {'__module__': '__main__', '__init__': <function Chinese.__init__ at 0x0000000001D40EA0>, '__dict__': <attribute '__dict__' of 'Chinese' objects>, '__weakref__': <attribute '__weakref__' of 'Chinese' objects>, '__doc__': None, 'country': '中国', 'eat': <function eat at 0x0000000001DFC7B8>}

#3实例类属性
print("----------开始修改实例类属性")
#3.1 查看实例类属性
p=Chinese('张三',18,"")
print(p.country) #中国
#3.2 修改实例类属性
p.country="中华人民共和国"
print(p.country) #中华人民共和国
#3.3 删除实例类属性
del p.country
print(p.__dict__)
#{'name': '张三', 'age': 18, 'gender': '男'}
# 3.4 添加类属性
p.country="中国"
print(p.__dict__)
#{'name': '张三', 'age': 18, 'gender': '男', 'country': '中国'}

print("----------开始修改实例属性")
#4.1 查看实例类属性
p=Chinese('张三',18,"")
print(p.name) #张三
#4.2 修改实例类属性
p.name="张三三"
print(p.name) #张三三
#4.3 删除实例类属性
del p.name
print(p.__dict__)
#{'age': 18, 'gender': '男'}
# 4.4 添加类属性
p.name="张三三"
print(p.__dict__)
#{'age': 18, 'gender': '男', 'name': '张三三'}

print("----------开始修改实例方法")
#4.1 查看实例方法
p=Chinese('张三',18,"")
p.eat('包子')
# 张三 正在吃 包子
#4.2 修改实例类属性
def eat(self,food,address):
    print('%s在%s吃%s' %(self.name,address,food))
p.eat=eat
p.name="张三三"
p.eat(p,"包子","食堂") #张三三在食堂吃包子
#4.3 删除实例类属性
del p.eat
print(p.__dict__)
#{'name': '张三三', 'age': 18, 'gender': '男'}
# 4.4 添加实例方法属性
def study(self,cont):
    print('%s学%s' % (self.name,cont))
p.study = study
p.study(p,'python')
print(p.__dict__)
#张三三学python
# {'name': '张三三', 'age': 18, 'gender': '男', 'study': <function study at 0x0000000002526B70>}
ViewCode

  类是什么时候被加载的,以及类名是什么时候生效的?

    创建一个类的空间,然后从上到下加载内容(属性、方法)。然后把这个命名空间指向类名。

  

   为什么Chinese.play与p.play的内存地址不一样?

    p.play其实存的不是func的内存地址,而是存着func内存地址的一个变量的地址。类似:Chinese.play存的是你的家庭地址。而p.play存的是地址簿的第二页。    

2 静态属性、静态方法、类方法

class Room:
    owner="China"
    # 这是一个房间的类
    def __init__(self,name,width,length,height):
        self.name=name
        self.width=width
        self.length =length
        self.height=height
    @property
    # 这是一个方法,但是通过@property装饰后,就变成了一个静态方法,可以通过.方法名调用
    def getArea(self):
        return self.width*self.length
    @classmethod
    # 这是一个方法,但是通过@classmethod装饰后,就变成了一个类方法,可以类调用,不用实例化
    def getOwner(cls):
        print("房产所有者"+cls.owner)
    @staticmethod
    # 这是一个方法,但是通过@staticmethod装饰后,就变成了一个静态方法,可以在调用处直接传递参数
    # 用户输入想要的面积和折扣,可以计算出价格
    def getPrice(area,discount):
       return area*20000*discount/10

r1=Room("天字一号房",10,20,2)
print('房间面积'+str(r1.getArea)) #房间面积200
#类方法:可以通过类名直接调用方法
Room.getOwner() #房产所有者China
#静态方法,可以传递一些与类关联不大的数据
print(r1.getPrice(r1.getArea,9)) #3600000.0
View Code

总结:

静态属性 @property 静态方法 @staticmethod 类方法 @classmethod
只读的,可以当属性使用
和类有关,和参数关系不密切
一个cls参数

实例.func

实例.func() 类.func(),类实例.func()

3 组合(大类包含小类) 什么有什么的关系

class School:
    # 这是一个学校类
    def __init__(self,name,address):
        self.name=name
        self.address=address
class Courses:
    # 这是一个课程类,组合了一个学校
    def __init__(self, name, cycle,price,schoolObj):
        self.name = name
        self.cycle = cycle
        self.price = price
        self.school = schoolObj
    def getInfo(self):
        print('%s 正在开设 %s' %(self.school.name,self.name))
school = School('北大','北京')
Courses('python','100','10',school).getInfo()
View Code

4 继承(子类继承父类) 什么是什么的关系

#继承一:抽象出共有的父类,实现继承
class Animal:
    def __init__(self,name):
        self.name=name
    def eat(self):
        print('%s会吃' %(self.name))
    def bark(self):
        print('%s会叫' %self.name)
class Dog(Animal):
    pass
class Cat(Animal):
    pass

dog=Dog("小黄")
dog.bark()
cat = Cat("伊丽莎白")
cat.eat()
#继承二:但是猫和狗的叫声不同,所以常用的是接口继承(父类只提供一个规范,子类在调用时需要实现这些规范)
# 如果是抽象方法,子类必须实现
import abc
class Animal(metaclass=abc.ABCMeta):
    def __init__(self,name):
        self.name=name
    @abc.abstractclassmethod
    def eat(self):
        print('%s会吃' %(self.name))
    def bark(self):
        print('%s会叫' %self.name)
class Dog(Animal):
    def bark(self):
        print('%s会汪汪叫' %self.name)
    #如果是抽象方法,子类必须实现
    def eat(self):
        pass
class Cat(Animal):
    def eat(self):
        print('%s会吃鱼儿' %self.name)

dog=Dog("小黄")
dog.bark()
cat = Cat("伊丽莎白")
cat.eat()
两种继承方式

  4.1 继承顺序

  当类是经典类(class C1:)时,多继承情况下,深度优先(依次找到根节点)

  当类是新式类(class C1(object):)时,多继承情况下,广度优先(最后一次才找到根节点)

  原理:python会计算一个方法解析顺序(MRO)列表,可通过print(类名.__mro__)查看

  4.2 子类中调用父类的方法

class Vehicle:
    def __init__(self,name,speed,load):
        self.name=name
        self.speed = speed
        self.load = load
    def run(self):
        print('%s开始启动' %(self.name))

class Subway(Vehicle):
    def __init__(self,name,speed,load,line):
        Vehicle.__init__(self,name,speed,load)
        self.line = line
    def run(self):
        Vehicle.run(self)
        print("时速:"+self.speed)
class Car(Vehicle):
    # 使用super减少了self字段的传递,而且如果父类类名修改的话,子类继承不用改变
    def __init__(self,name,speed,load,brand):
        super().__init__(name,speed,load)
        self.brand = brand
    def run(self):
        super().run()
        print("品牌:"+self.brand)

#测试数据
sub=Subway("地铁","300km/h","20000","2号线")
sub.run()

cr=Car("汽车","80km/h","5","红旗")
cr.run()
两种方式实现继承

 5 多态

  类的继承有两成意义:改变+扩展。多态就是累的这两层意义的一个具体的实现机制。在不考虑实例(子类)类型的情况下,使用子类。即调用不同的实例化对象下的相同方法,时间的过程不同

class H2O:
    def __init__(self,name,temp):
        self.name=name
        self.temp = temp
    def currentStatus(self):
        if self.temp < 0:
            print("在101.325kPa下,当前状态为:冰")
        elif self.temp == 0:
            print("在101.325kPa下,当前状态可能是:冰水混合物")
        elif self.temp < 100:
            print("在101.325kPa下,当前状态为:水")
        elif self.temp > 100:
            print("在101.325kPa下,当前状态为:水蒸气")
class Ice(H2O):
    pass
class Water(H2O):
    pass
class Steam(H2O):
    pass

ice = Ice("",-10)
water = Water("",10)
steam= Steam("水蒸气",101)

ice.currentStatus();water.currentStatus();steam.currentStatus();
水的三种状态

 6 封装 明确区分内外,其实不能真正不被外部访问。内部实现逻辑,外部无需知晓,并且为封装到内部的逻辑提供一个供外部访问的接口  

 约定:_开头定义

 __开头,其实在外部调用的时候python默认修改了名“_类名_定义名“

class People:
    _plate="地球"
    def __init__(self,name,age):
        self.name=name
        self._age=age

p=People("张三",12)
print(p._plate) #地球
print(p._age)   #12
'''虽然以_开头但是还可以被访问到,因为这只是一种约定'''
class People2:
    __plate="地球"
    def __init__(self,name,age):
        self.name=name
        self.__age=age

p=People2("张三",12)
#print(p.__plate) #报错 AttributeError: 'People2' object has no attribute '__plate'
print(p._People2__plate)  #地球
print(p._People2__age)   #12
'''虽然以__开头不可以被直接访问到,但可以通过“_+类名+自定义名称'''
class People3:
    __plate="地球"
    def __init__(self,name,age):
        self.name=name
        self.__age=age
    # 外部现在是无法通过属性名调用了,但是目前有一个需求:就是要获取年龄,这个时候还可以通过定义接口的方法实现
    def getAge(self):
        return self.__age;
p3=People3("张三",12)
print(p3.getAge())
View Code

没有必要的封装,不可取

7 反射:自省,自己检测自己。主要指程序可以访问、检测和修改它本身状态或行为的一种能力。

    用字符串类型的变量名来访问变量值。类似XX.YY 这种形式,都可以通过反射来访问。类、对象、模块

# 反射:自省,自己检测自己。主要指程序可以访问、检测和修改它本身状态或行为的一种能力
class Animal:
    able="Run"
    def __init__(self,name):
        self.name=name
    def eat(self):
        print('%s会吃' %(self.name))
    def bark(self):
        print('%s会叫' %self.name)
a1=Animal("Dog")
print(a1.name)
print(a1.__dict__["name"])
# a1.name 等价于 a1.__dict__["name"]
# 1 hasattr(object,name) 判断object中有没有一个name字符串对应的方法或属性
print(hasattr(a1,'able')) #True
print(hasattr(a1,'eat')) #True
print(a1.__dict__)  #{'name': 'Dog'}
# 虽然__dict__中没有eat方法,但是,这里只是检查能否调用到
# 2 getattr(object,name) 等价于 a1.属性或方法  获取对应的方法或属性
print(getattr(a1,'able')) #Run
print(getattr(a1,'eat')) #<bound method Animal.eat of <__main__.Animal object at 0x0000000002564630>>
# 如果没有就报错,通过默认参数的方法可以不报错
print(getattr(a1,'11111',"该属性不存在"))
print(getattr(a1,'2222',"该方法不存在"))
# 3 getattr(object,k,v) 等价于 a1.属性或方法=''  判断设置对应的方法或属性
setattr(a1,'able2','run')  #创建
setattr(a1,'able','drink') #修改
print(getattr(a1,'able')) #drink
print(getattr(a1,'able2')) #run
# 4 delattr(object,k,v) 删除对应的方法或属性
delattr(a1,'able')
View Code
# 反射:自省,自己检测自己。主要指程序可以访问、检测和修改它本身状态或行为的一种能力
class Animal:
    able="Run"
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def eat(self):
        print('%s会吃' %(self.name))
    def bark(self):
        print('%s会叫' %self.name)

    def __getattr__(self, item):
        print("调用__getattr__(self, item)方法")
    # __setattr__()方法不常用,但可以通过重写该方法,实现自己特殊的需求,如类型只能是字符串
    def __setattr__(self, key, value):
        if key.strip()=="name":
            if type(value) is str:
                self.__dict__[key] = value
            else:
                print("name必须是字符串")
        else:
            self.__dict__[key] = value
    def __delattr__(self,item):
        self.__dict__.pop(item)
a1=Animal("Dog",12)
# __开头表示内置属性
# 修改属性
a1.__setattr__('name',15)
print(a1.__dict__) #{'name': 'Dog', 'age': 12}
del a1.name #调用__delattr__(self, item)方法
# 只有在属性不存在的时候会触发__getattr__
print(a1.abccccc)
反射2
# 导入其他模块进行反射
import pkage1.say as p

print(hasattr(p,"sayHello"))
print(getattr(p,"sayHello111","不存在"))
导入其他模块进行反射
# 反射可以使代码更灵活,易于扩展
# Why?因为反射可以根据字符串去获取、访问变量

class Manager:
    # 定义成操作列表,列表的优势是可以通过下标索引来访问
    OPERATE_DIC = [
        ('创建班级', 'create_class'),
        ('创建课程', 'create_course'),
        ('创建学生', 'create_student'),
    ]

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

    def create_class(self):
        print("创建班级")

    def create_course(self):
        print("创建课程")

    def create_student(self):
        print("创建学生")


class Student:
    OPERATE_DIC = [
        ('选择班级', 'choose_class'),
        ('选择课程', 'choose_course'),
    ]

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

    def choose_class(self):
        print("选择班级")

    def choose_course(self):
        print("选择课程")


def login():
    userName = input("账号:")
    pwd = input("密码:")
    with open("用户表") as f:
        for line in f:
            user, password, role = line.strip().split("|")
            if user == userName and password == pwd:
                return user, role
        else:
            print("用户不存在")


def main():
    user, role = login()
    if user and role:
        print("欢迎%s:%s" % (role, user))
        # 根据用户角色反射出用户类--开始
        # 1:自己文件反射需要引入包
        import sys
        # 2:根据字符串去获取文件中的类(灵活的判断是Manager还是Student)
        cls = getattr(sys.modules["__main__"], role)
        # 3:根据类创建对象
        obj = cls(user)
        op_dic = obj.OPERATE_DIC
        # 4: 有了对象,调用发方法
        while True:
            #enumerate方法是将op_dic 组合为一个索引序列,同时列出数据和数据下标
            for num,i in enumerate(op_dic,1):
                print(num,i)
            chooseNum= int(input("请输入操作序号:"))
            # 根据用户输入的操作序号,获取相应的操作方法名,字符串
            op_fun = op_dic[chooseNum-1][1]
            #通过对象的反射,对象.方法名 ;调用
            getattr(obj,op_fun)()
main()
反射典型--登录系统
# 反射场景:
# 表:网关、夜灯、呼叫器、上下线记录表
# 关系:一个网关下有多个夜灯、呼叫器,子表中通过(网关的id)关联网关表,
#       所有的设备(网关、夜灯、呼叫器)的上下线记录都会存储在上下线记录表中(设备id,设备类型)
#  我们根据记录表中的设备id要反向关联出是设备的详细信息,如何处理。
  from deviceApp import models as TModel
# 首先定义一个枚举类型(light【设备类型,网关表中存储的数据】,"NightLightDevice" Model类中的名字)
# @unique
# class DeviceType(Enum):
#     # 定义设备类型,根据设备类型对应不同
#     light = "NightLightDevice"
#     gate = "GatewayDevice"
#     call = "CallbuttonDevice"
    
    
    
#     deviceType = request['deviceType']  # 设备产品  例如call
# # 1. 根据deviceType关联枚举类型,利用反射获取相应的model类
#    cls = getattr(TModel, DeviceType[deviceType].value, None)
#    if cls:
#        # 如果model类存在,根据主键id,找到对应的设备实体
#        child_device_obj = cls.objects.get(id=deviceId)
#    else:
#        return failResultJson(msg='getattr-TModel对象失败')
反射应用场景

8 二次加工标准类型

# 方法1,通过继承实现
class ListYK(list):
    # 添加一个新方法,求取中间值
    def show_middle(self):
        mid_index=int(len(self)/2)
        return self[mid_index]

    # 重写一个添加方法,只能添加字符串
    def append(self,p_object):
        if type(p_object) is str:
            # list.append(self,p_object)
            super().append(p_object)
l1=ListYK("hello")
print(l1.show_middle()) #l
l1.append("1")
l1.append(2)
print(l1) #['h', 'e', 'l', 'l', 'o', '1']

# 2授权:授权时包装的一个特性,包装一个类型通常是对已存在的类型的一些定制
# 授权的过程,及时所有更新的功能都是有新类的某部分来处理,但已存在的功能就授权给队形的默认属性
import time
class OpenNew:
    def __init__(self,filename,mode='r',encoding="utf-8"):
        self.file=open(filename,mode,encoding=encoding)
        self.filename=filename
        self.mode = mode
        self.encoding = encoding
    def __getattr__(self, item):
        return getattr(self.file,item)
    def write(self,line):
        t = time.strftime('%Y-%m-%d %T')
        self.file.write('%s %s' % (t, line))
f1=OpenNew("a.txt","w+","utf-8")
f1.write("111
")
f1.write("222")
View Code

9 __new__

 1 class Single:
 2      def __new__(cls, *args, **kwargs):
 3          obj = object.__new__(cls)     #类名() 调用了new方法,开辟可一个空间给obj。
 4          print("init中的self:", obj)
 5          return obj
 6      def __init__(self):
 7          print("init中的self:",self)
 8 sin=Single()
 9 
10 # 输出结果,
11 # init中的self: <__main__.Single object at 0x00CDA930>
12 # init中的self: <__main__.Single object at 0x00CDA930>
13 
14 # 结论:line 8 :类名() 调用了new方法,开辟可一个空间给obj。
15 #       然后把obj的空间给self。#       
16 #       line 6 :执行__init__ 方法
17 #       最后把空间给调用者sin
__new__
class Single:
    ISINCTANCE=None
    def __new__(cls, *args, **kwargs):
        if not cls.ISINCTANCE:
            cls.ISINCTANCE = object.__new__(cls)
        return cls.ISINCTANCE

    def __init__(self, *args, **kwargs):
        pass


a = Single()
print(a)
b = Single()
print(b)

''' 输出结果
<__main__.Single object at 0x00F5FE70>
<__main__.Single object at 0x00F5FE70>
'''
# 结论,先定义属性,new的时候判断该属性是否有值,如果有值返回。没有值再new值
单例

10 __getattribute__与__getattr__

#__getattribute__与__getattr__

# 1:只有__getattr__存在的情况下,只有获取的属性不存在,才触发此方法

class Foo:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def getInfo(self):
        return "My name is %s,I am %s years old" % (self.name, self.age)
    def __getattr__(self,item):
        print("我是__getattr")

f = Foo("Mr.zhang", 18)
f.name
f.agea  #只有属性不存在的时候,才会触发

class Foo2:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def getInfo(self):
        return "My name is %s,I am %s years old" % (self.name, self.age)
    def __getattribute__(self, item):
        print("我是__getattribute__")

f2 = Foo2("Mr.zhang", 18)
f2.name
f2.agea
#不管是否存在,都会触发__getattribute__方法
class Foo3:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def getInfo(self):
        return "My name is %s,I am %s years old" % (self.name, self.age)

    def __getattribute__(self, item):
        print("我是__getattribute__")
        raise AttributeError("属性不存在,就会触发getattr方法")
    def __getattr__(self, item):
        print("我是__getattr")
print("F3=======>")
f3 = Foo3("Mr.zhang", 18)
f3.name
f3.agea
# 不管是否存在,都会触发__getattribute__方法,当都_getattribute__方法抛出AttributeError异常时,触发__getattr__方法
#__getattribute__与__getattr__

11 __item__

class Foo:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def getInfo(self):
        return "My name is %s,I am %s years old" % (self.name, self.age)
    def __getitem__(self, item):
        print("我是__getitem__方法")
        return self.__dict__[item]
    def __setitem__(self, key, value):
         self.__dict__[key]=value
    def __delitem__(self, key):
        self.__dict__.pop(self.__dict__[key])


f = Foo("Mr.zhang", 18)
print(f["name"])
f["age"]="12"
print(f["age"])
del  f.age
print(f.__dict__)
'''
我是__getitem__方法
Mr.zhang
我是__getitem__方法
12
{'name': 'Mr.zhang'}
'''
View Code

12 __str__与__repr__

# 注意:这两个方法的返回值必须是字符串
# 如果__str__没有定义,那么就会__repr__替代
class Foo:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def __str__(self):
        return ("我叫%s 今年%s岁了" %(self.name,self.age))

    def __repr__(self):
        return ("我今年%s岁了 叫%s " % (self.age,self.name))

f=Foo("杨可",26)
print(f)
#str函数或者print函数 --》obj.__str__()
#repr或者交互式解释器--》obj.__repr__()
#如果__str__没有被定义,那么就会使用__repr__来代替
__str__ __repr__

13 __len__

class Student:
    def __len__(self):
        return len(self.name)
    def __init__(self,name):
        self.name=name

st=Student("张三")
print(len(st))
st=Student("尼古拉斯赵四儿")
print(len(st))
__len__

14 __hash__

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

    def __hash__(self):
        return hash('%s%s' % (self.name, self.sex))

    def __eq__(self, other):
        if self.name == other.name and self.sex == other.sex:
            return True


employ_lst = []
for i in range(1000):
    if i % 3 == 0:
        employ_lst.append(Employee('张三', 'male', i, 'python'))
    elif i % 3 == 1:
        employ_lst.append(Employee('李四', 'male', i, 'python'))
    else:
        employ_lst.append(Employee('王五', 'male', i, 'python'))
employ_newList = set(employ_lst)
for i in employ_newList:
    print(i.name, i.sex, i.age)
'''
1:定义一个员工类(Employee)
2:对象的属性 : 姓名 性别 年龄 部门
3:重写__hash__方法
4:重写__eq__方法
5:初始化数据
6:set去重
'''

'''输出结果
李四 male 1
张三 male 0
王五 male 2
'''
员工去重

15 查看实例的来源

from pkage1.Foo import Foo

f = Foo()
print(f.__module__)
print(f.__class__)
# pkage1.Foo
# <class 'pkage1.Foo.Foo'>
View Code

16 析构函数

class Foo:
    def __init__(self,name):
        self.name=name
    def __del__(self):
        print("析构函数")
f = Foo("zhangsan")
del f
View Code

17 __call__    利用 ()可以执行

class Foo:
    def __call__(self, *args, **kwargs):
        print("实例执行了call方法")
f=Foo()
f()
# 实例执行了call方法
call方法
class Foo:
    def __call__(self, *args, **kwargs):
        print("实例执行了call方法")


class Foooo:
    def __init__(self,cls):
        self.c=cls()  #实例化传递过来的cls,也就是Foo
        self.c()    # ()调用Foo中的__call__ 方法

Foooo(Foo)
在源码中比较常用的()

18 迭代器协议

# 迭代器协议:有一个iter方法,有一个next方法,有Stopiteration终止
class Foo:

    def __init__(self,x,prv=0,cur=1):
        self.x=x
        self.prv = prv
        self.cur = cur
    def __iter__(self):
        return self
    def __next__(self):
        if self.x>100:
            raise StopIteration("越界了")
        self.x =  self.prv + self.cur
        self.prv =self.cur
        self.cur= self.x
        return self.cur
f =Foo(10)
print(f.__next__())
print(next(f))

print("====>")
for i in f:
    print(i)

# 1
# 2
# ====>
# 3
# 5
# 8
# 13
# 21
# 34
# 55
# 89
# 144
斐波那契数列

 19 上下文管理协议

# 上下文管理协议
# with obj as  f 等同于 f=obj.__enter__()
# 执行代码块
# 一:没有异常,整个代码块运行完毕后触发__exit__,它的三个参数都为None
# 二:出现异常,直接触发__exit__
#     a:如果__exit__的返回值为True,代表吞掉了异常
#     b:如果__exit__的返回值不为True,代表吐出来异常
#     c:__exit__的运行完毕,代表整个with语句执行完毕
class OpenNew:
    def __init__(self,name):
        self.name = name
    def __enter__(self):
        print('执行enter')
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("执行exit方法")
        print("3")
        print(aa)
        print(exc_type)
        print(exc_val)
        print(exc_tb)
        print("4")
        return True
with OpenNew("a.txt") as  f:
    print('1')
    print('2')
print('5')
View Code

20 描述符

#描述符协议:描述符本质就是一个新式类,至少实现了__get__(),__set__(),__delete__()中的至少一个
# __get__(),调用属性时触发
# __set__(),为属性赋值是触发
# __delete__(),删除属性时触发
# 描述符作用:可以用来定义一个类的属性,
# 描述符的特性:由该类产生的实例变化时候不会调用这些个方法,定义另外一个属性时,会被调用
# 描述符分类:没有实现__set__()的叫非数据描述符,实现了__set__()和__get__()的叫数据描述符
# 注意事项:1:描述符本身和被代理类都是新式类,2:定义成类的属性,,不能定义到构造函数中
#定义一个描述符
class StrNew:
    def __get__(self, instance, owner):
        print("Str调用__get__")
    def __set__(self, instance, value):
        print("Str调用__set__")
    def __delete__(self, instance):
        print("Str调用__delete__")
class IntNew:
    def __get__(self, instance, owner):
        print("Int调用__get__")
    def __set__(self, instance, value):
        print("Int调用__set__")
    def __delete__(self, instance):
        print("Int调用__delete__")
class People:
    name=StrNew()
    age=IntNew()
    def __init__(self,name,age):
        self.name=name
        self.age=age
p=People('张三',15)
p.name
p.name="张三New"
del  p.name
p.age
p.age=15
del p.age

# 优先级
# 1:类属性>2:数据描述符>3:实例属性>4:非数据描述符>5:找不到属性触发__getattr__()
# print("1:类属性>2:数据描述符")
# People.name #调用类属性name 本质是调用描述符__get__()方法
# People.name="zhangsan" #它拥有更高的优先级,相当于覆盖了描述符,肯定不会触发描述符的
# del People.name

print("2:数据描述符>3:实例属性")
p=People('张三',15)
p.name="lisi"
print(p.__dict__)  #{}
# 与实例相关的属性字典中没有name,因为name是一个数据描述符,优先级高于实例属性,操作都是跟描述符有关,与实例无关了
描述符协议
# python 是弱类型,可以通过描述将其设置为强类型
# 定义一个类型控制的描述符
class TypeControl:
    # 定义一个构造函数,用于初始化,字段名称(根据名称对其进行设置)和类型(设置值是做判断)
    def __init__(self,name,typeName):
        self.name=name
        self.typeName = typeName
    def __get__(self, instance, owner):
        print("调用__get__")
        return instance.__dict__[self.name]
    def __set__(self, instance, value):
        print("调用__set__")
        # //如果设置值的属性和定义的属性不一致,报错
        if not isinstance(value,self.typeName):
            raise TypeError
        instance.__dict__[self.name]=value
    def __delete__(self, instance):
        print("调用__delete__")
        instance.__dict__.pop(self.name)

class People:
    name=TypeControl("name",str)
    age=TypeControl("age",int)
    def __init__(self,name,age):
        self.name=name
        self.age=age

p=People('张三',15)
p.name
del p.name

p=People('1',1)
p.name
del p.name
定义一个类型控制的描述符

21 装饰器

# 装饰器=高阶函数+函数嵌套+闭包
# 高阶函数:传入参数或输出结果是一个函数
# 函数嵌套:函数中定义函数
import time
# 添加一个参数,如果参数是n就打n折
def disCount(n=1):
    def timmer(func):
        def wrapper(*args,**kwargs):
            startTime= time.time()
            res=func(*args,**kwargs)*n;
            endTime= time.time()
            print("今天是国庆节,每位客户打的折扣为:"+str(n*10))
            return  res
        return wrapper
    return timmer
#@timmer  #语法糖,相当于#test=timmer(test)
@disCount(n=0.9)
def test(a,b):
    return a+b

print(test(100,200))
装饰器
# 定义一个用户列表
userList=[{"name":"zhangsan","pwd":"333"},{"name":"lisi","pwd":"444"},{"name":"wangwu","pwd":"555"}]
currentUser={"name":"None","loginState":"False"}
def SignOut():
    currentUser = {"name": "None", "loginState": "False"}

def authority(func):
    def wapper(*args,**kwargs):
        if currentUser["name"] and eval(currentUser["loginState"]):
            res = func(*args,**kwargs)
            return res
        else:
            SignIn();
    return wapper
def index():
    print("欢迎来到登录页面")
@authority
def home():
    print("欢迎回家%s" %currentUser["name"])
@authority
def shopping_car():
    print("%s的购物车中有:苹果、葡萄。" %currentUser["name"])

def SignIn():
    print("请先登录")
    userName = input('用户名:').strip()
    userPwd = input('密码:').strip()
    for userInfo in userList:
        if userInfo["name"] == userName and userInfo["pwd"] == userPwd:
            currentUser["name"] = userName
            currentUser["loginState"] = "True"
    if currentUser["name"] and eval(currentUser["loginState"]):
        print("登录成功,")
    else:
        print("用户名或密码错误")
        SignIn()
index()
home()
shopping_car()
装饰器,修饰方法
class TypeControl:
    # 定义一个构造函数,用于初始化,字段名称(根据名称对其进行设置)和类型(设置值是做判断)
    def __init__(self,name,typeName):
        self.name=name
        self.typeName = typeName
    def __get__(self, instance, owner):
        print("调用__get__")
        return instance.__dict__[self.name]
    def __set__(self, instance, value):
        print("调用__set__")
        # //如果设置值的属性和定义的属性不一致,报错
        if not isinstance(value,self.typeName):
            raise TypeError
        instance.__dict__[self.name]=value
    def __delete__(self, instance):
        print("调用__delete__")
        instance.__dict__.pop(self.name)

def typeAssert(**kwargs):
    def decorateClass(cls):
        for name,expected_type in kwargs.items():
            setattr(cls,name,TypeControl(name,expected_type)) #People.name = TypeControl('name',str)
        return cls
    return decorateClass

#传递字典参数,运行typeAssert(),返回decorateClass
# 继续执行@decorateClass
# 执行@decorateClass给类添加属性
@typeAssert(name=str,age=int)
class People:
    def __init__(self,name,age):
        self.name=name
        self.age=age

p=People('张三',15)
p.name
del p.name
装饰器,修饰类

22 自定义Property属性

class myProperty:
    def __init__(self,func):
        self.func=func
    def __get__(self, instance, owner):
        # 描述符中调用func函数
        # 直接用类调用会由于instance是None,导致运行func()报错
        if instance is None:
            return self
        return self.func(instance)
class Room:
    def __init__(self,name,length,width):
        self.name=name
        self.length=length
        self.width = width
    @myProperty #getArea=myProperty(getArea)
    def getArea(self):
        return self.width*self.length
r1=Room("卧室",6,4)
print(r1.getArea)
print(Room.getArea)
# 思路@myProperty相当于getArea=myProperty(getArea)
#1:定义一个getArea=myProperty(getArea)
#2:其中定义一个构造函数,接收传递过来的函数名并返回,相当于类的装饰器功能
#3:要想通过.方法名掉用方法。需要通过描述符(3:实例属性>4:非数据描述符)
自定义属性myProperty
class myProperty:
    def __init__(self,func):
        self.func=func
    def __get__(self, instance, owner):
        print("get")
        # 描述符中调用func函数
        # 直接用类调用会由于instance是None,导致运行func()报错
        if instance is None:
            return self
        res = self.func(instance)
        setattr(instance,self.func.__name__,res)
        return res
class Room:
    def __init__(self,name,length,width):
        self.name=name
        self.length=length
        self.width = width
    @myProperty #getArea=myProperty(getArea)
    def getArea(self):
        return self.width*self.length
r1=Room("卧室",6,4)
print(r1.getArea)
print(Room.getArea)
print("=======>懒加载,只调用一次,原理:把getArea添加到r1的数据字典中,然后直接从字典中获取")
print(r1.getArea)
print(r1.getArea)
print(r1.getArea)

# 思路@myProperty相当于getArea=myProperty(getArea)
#1:定义一个getArea=myProperty(getArea)
#2:其中定义一个构造函数,接收传递过来的函数名并返回,相当于类的装饰器功能
#3:要想通过.方法名掉用方法。需要通过描述符(3:实例属性>4:非数据描述符)
懒加载myProperty

 23 元类

# 我们用class关键字定义的类本身也是一个对象,负责产生该对象的类称之为元类(元类可以简称为类的类),内置的元类为type
# print(type(Foo)) # 结果为<class 'type'>,证明是调用了type这个元类而产生的Foo,即默认的元类为type
#
# class关键字在帮我们创建类时,必然帮我们调用了元类OldboyTeacher=type(...),那调用type时传入的参数是什么呢?必然是类的关键组成部分,一个类有三大组成部分,分别是
# 1、类名class_name='Foo'
# 2、基类们class_bases=(object,)
# 3、类的名称空间class_dic,类的名称空间是执行类体代码而得到的
# 调用type时会依次传入以上三个参数
class MyType(type):
    def __init__(self):
        print("")
    def __call__(self, *args, **kwargs):
        # 此时self 是Foo,
        #object.__new__(self) 相当于以Foo为类创建对象,即:产生f1
        obj=object.__new__(self)
        #调用Foo下面的init方法
        self.__init__(self,*args, **kwargs)
class Foo(metaclass=MyType):
    def __init__(self,name):
        self.name=name
View Code

 24 总结

  • 面向对象的过程
    • 1 开辟一个空间__new__(),属于对象的
    • 2 把对象的空间传递给self,执行__init__()方法
    • 3 把对象空间返还给调用者
  • 内置方法、双下方法、魔术方法 
    • 1 格式:__方法名__
    • 内置函数和类的内置方法之间联系紧密
    • 常见的内置方法有 __new__ __init__ __call__ __str__...
  • set去重原理
    • set() 函数中会先调用对象的 __hash__() 方法,获取 hash 结果;
    • 如果 hash 结果相同,用比较操作符 == (也就是调用函数 __eq__())判断二者的值是否相等;
    • 如果都相等,去重;否则,set() 认为二者不同,两个都保留到结果中。
原文地址:https://www.cnblogs.com/YK2012/p/9748654.html