首页 > Python > python基础笔记

python基础笔记

2019-09-13

一、继承

'''
1、什么是继承
    继承是一种新建类的方式,新建的类称之为子类,被继承的类称之为基类、父类、超类
    继承描述的是一种“遗传”的关系:子类可以重用父类的属性

在python中的继承注意两点:
   1. 在python中支持一个子类同时继承多个父类,
   2. python中类分为两种:
            新式类:但凡继承object的类,以及该类的子类。。。都是新式类
                在python3中一个类如果没有继承人类类,默认继承object类,即python3中所有的类都是新式类

            经典类: 没有继承object的类,以及该类的子类。。。都是经典类
                在python2中才区分新式类与经典类

2、为何要用继承
     减少代码冗余
3、如何用继承

class Parent1(object):
    pass
class Parent2:
    pass
class Subclass1(Parent1,Parent2):
    pass
print(Subclass1.__bases__)


# 2、在继承的背景下,属性查找的优先级
      #当类是经典类时,多继承的情况下,在要查找的属性不存在时,会按照深度优先的方式查找下去
      #当类是新式类时,多继承的情况下,在要查找的属性不存在时,会按照广度优先的方式查找下去
继承解决的是类与类之间的代码冗余问题,一定是一个类是另外一个类的子类,总结对象之间的相似之处得到类,总结类与类之间的相似之处就得到了类们的父类

多继承背景下属性查找的顺序:对象-》对象的类-》按照从左往右的顺序一个一个的分支找下去
# 一旦出现菱形继承问题,新式类与经典类在属性查找上的区别是
# 新式类:广度优先查找,在最后一个分支查找顶级类
# 经典类:深度优先查找,在第一个分支就查找顶级类

# 在子类派生出的新方法中重用父类功能的方式一:
# 指名道姓地访问某一个类的函数
# 注意:
# 1. 该方式与继承是没有关系的
# 2. 访问是某一个类的函数,没有自动传值的效果
class OldboyPeople:
    school='Oldboy'

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


class OldboyStudent(OldboyPeople):

    #           stu1,'李特丹',18,'female'
    def __init__(self,name,age,sex,num=0):
        OldboyPeople.__init__(self,name,age,sex) #OldboyPeople.__init__(stu1,李特丹',18,'female')

        self.score=num

    def choose_course(self):
        print('%s is choosing course' %self.name)



class OldboyTeacher(OldboyPeople):

    def __init__(self,name,age,sex,level):
        OldboyPeople.__init__(self,name,age,sex)

        self.level=level

    def score(self,stu,num):
        stu.score=num


stu1=OldboyStudent('李特丹',18,'female') #OldboyStudent.__init__(stu1,'李特丹',18,'female')
print(stu1.__dict__)

tea1=OldboyTeacher('egon',18,'male',10) ##OldboyTeacher.__init__(tea1,'egon',18,'male',10)
print(tea1.__dict__)
code
在子类派生出的新方法中重用父类功能的方式二:只能在子类中用
在python2:super(自己的类名,对象自己)
在python3:super()
调用super()会得到一个特殊的对象,该特殊的对象是专门用来引用父类中的属性的,!!!完全参照mro列表!!!
注意:
# 1. 该方式与继承严格依赖于继承的mro列表
# 2. 访问是绑定方法,有自动传值的效果
class OldboyPeople:
    school='Oldboy'

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


class OldboyStudent(OldboyPeople):


    def __init__(self,name,age,sex,num=0):
        # OldboyPeople.__init__(self,name,age,sex) #OldboyPeople.__init__(stu1,李特丹',18,'female')
        super(OldboyStudent,self).__init__(name,age,sex)

        self.score=num

    def choose_course(self):
        print('%s is choosing course' %self.name)



class OldboyTeacher(OldboyPeople):

    def __init__(self,name,age,sex,level):
        super().__init__(name,age,sex)

        self.level=level

    def score(self,stu,num):
        stu.score=num

#例子
class A:
    def test(self):
        print('A.test()')
        super().test()

class B:
    def test(self):
        print('from B')

class C(A,B):
    pass

obj=C()
print(C.mro())
#[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
obj.test()
'''
A.test()
from B
'''
code

类的继承有两层意义:

1.改变???? 2.扩展

   组合指的是一个对象拥有某一个属性,该属性的值是另外一个类的对象
   利用组合可以减少类与类之间代码冗余


二、多态

1 多态
多态指的是同一种事物的多种形态
2 多态性:
可以在不用考虑对象具体类型的情况下而直接使用对象
优点:归一化,简化对象的使用

? 多态就是类的这两层意义的一个具体的实现机制,即,调用不同的类实例化得对象下的相同的方法,实现的过程不一样

?? python中的标准类型就是多态概念的一个很好的示范

import abc
class Animal(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def speak(self):
        pass
    @abc.abstractmethod
    def run(self):
        pass
# 抽象基类:是用来指定规范,但凡继承该类的子都必须实现speak和run,而名字必须叫speak和run
# 注意:不能实例化抽象基类
Animal()

class People(Animal):
    def speak(self):
        print('say hello')
    def run(self):
        pass
class Dog(Animal):
    def speak(self):
        print('汪汪汪')
    def run(self):
        pass
class Pig(Animal):
    def speak(self):
        print('哼哼哼哼哼')
    def run(self):
        pass
obj1=People()
obj2=Dog()
obj3=Pig()
# obj1,obj2,obj3都是动物
obj1.speak()
obj2.speak()
obj3.speak()
def speak(animal):
    animal.speak()
speak(obj1)
speak(obj2)
speak(obj3)
obj1=[1,2,3]
obj2='hello'
obj3={'x':1}
print(obj1.__len__())
print(obj2.__len__())
print(obj3.__len__())
print(len(obj1))
print(len(obj2))
print(len(obj3))
code

三、封装

1 什么是封装
装就是将数据属性或者函数属性存放到一个名称空间里
封指的是隐藏,该隐藏是为了明确地区分内外,即该隐藏是对外不对内(在类外部无法直接访问隐藏的属性,而在类内部是可以访问)
2 为何要封装
1. 封数据属性:???
2. 封函数属性:???
3 如何封装???
在类内定义的属性前加__开头

第一个层面的封装:类就是麻袋,这本身就是一种封装

第二个层面的封装:类中定义私有的,只在类的内部使用,外部无法访问

第三个层面的封装:明确区分内外,内部的实现逻辑,外部无法知晓,并且为封装到内部的逻辑提供一个访问接口给外部使用(这才是真正的封装)

class People:
    __country='China' #_People__country='China'
    __n=111           #_People__n=111

    def __init__(self,name):
        self.__name=name #self._People__name=name

    def run(self):
        print('%s is running' %self.__name) #self._People__name

print(People.__country)

obj=People('egon')
print(obj.__name)
print(obj.run)
obj.run()

print(People.__dict__)
print(People._People__country)
print(obj.__dict__)
print(obj._People__name)
需要注意的问题:
1. 这种隐藏只是一种语法上的变形,并没有真的限制访问
2. 这种变形只在类定义阶段检测语法时变形一次,类定义阶段之后新增的__开头的属性不会发生变形
3. 在继承中,父类如果不想让子类覆盖自己的方法,可以在该方法前加__开头
封装的真实意图:
把数据属性或函数属性装起来就是为了以后使用的,封起来即藏起来是为不让外部直接使用
1.封数据属性:把数据属性藏起来,是为了不让外部直接操作隐藏的属性,而通过类内开辟的接口来间接地操作属性,我们可以在接口之上附加任意的控制逻辑来严格控制使用者对属性的操作
    2. 封函数属性: 隔离复杂度

四、类方法与装饰器

 类中定义的函数有两大类(3小种)用途,一类是绑定方法,另外一类是非绑定方法

# 1. 绑定方法:
# 特点:绑定给谁就应该由谁来调用,谁来调用就会将谁当作第一个参数自动传入
# 1.1 绑定给对象的:类中定义的函数默认就是绑定对象的
# 1.2 绑定给类的:在类中定义的函数上加一个装饰器classmethod


# 2. 非绑定方法
# 特点: 既不与类绑定也不与对象绑定,意味着对象或者类都可以调用,但无论谁来调用都是一个普通函数,根本没有自动传值一说
class Foo:
    def func1(self):
        print('绑定给对象的方法',self)

    @classmethod
    def func2(cls):
        print('绑定给类的方法: ',cls)

    @staticmethod
    def func3():
        print('普通函数')

obj=Foo()
obj.func1()
print(obj)

Foo.func2()

# 绑定方法
print(obj.func1)
print(Foo.func2)

# 非绑定方法
print(obj.func3)
print(Foo.func3)

一 什么是装饰器?

器即函数,装饰即修饰,意指为其他函数添加新功能

装饰器定义:本质就是函数,功能是为其他函数添加新功能

property装饰器就是将一个函数属性伪装成一个数据属性

1.不修改被装饰函数的源代码(开放封闭原则)

2.为被装饰函数添加新功能后,不修改被修饰函数的调用方式

user_list=[
    {'name':'alex','passwd':'123'},
    {'name':'linhaifeng','passwd':'123'},
    {'name':'wupeiqi','passwd':'123'},
    {'name':'yuanhao','passwd':'123'},
]

current_user={'username':None,'login':False}
def auth(auth_type='file'):
    def auth_deco(func):
        def wrapper(*args,**kwargs):
            if auth_type == 'file':
                if current_user['username'] and current_user['login']:
                    res=func(*args,**kwargs)
                    return res
                username=input('用户名: ').strip()
                passwd=input('密码: ').strip()

                for index,user_dic in enumerate(user_list):
                    if username == user_dic['name'] and passwd == user_dic['passwd']:
                        current_user['username']=username
                        current_user['login']=True
                        res=func(*args,**kwargs)
                        return res
                else:
                    print('用户名或者密码错误,重新登录')
            elif auth_type == 'ldap':
                print('巴拉巴拉小魔仙')
                res=func(*args,**kwargs)
                return res
        return wrapper
    return auth_deco


#auth(auth_type='file')就是在运行一个函数,然后返回auth_deco,所以@auth(auth_type='file')
#就相当于@auth_deco,只不过现在,我们的auth_deco作为一个闭包的应用,外层的包auth给它留了一个auth_type='file'参数
@auth(auth_type='ldap')
def index():
    print('欢迎来到主页面')

@auth(auth_type='ldap')
def home():
    print('这里是你家')

def shopping_car():
    print('查看购物车啊亲')

def order():
    print('查看订单啊亲')

# print(user_list)
index()
# print(user_list)
home()

# 带参装饰器
View Code
def deco(func):
    print('---')
    func.x=1
    func.y=2
    func.z=3
    return func

#一切皆对象
@deco    #test=deco(test)
def test():
    print('test_func_run')
test.x=1
print(test.__dict__)

@deco  #Foo=deco(Foo)
class Foo:
    pass

f1=Foo()
print(f1.__dict__)
print(Foo.__dict__)
print(f1.x)
View Code
def Typed(**kwargs):
    def deco(obj):
        # print('+++++',kwargs)
        # print('obj_name',obj)
        for key,val in kwargs.items():
            # obj.__dict__[key]=[val]
            # obj.key=val
            setattr(obj,key,val)
        return obj
    # print('---',kwargs)
    return deco

@Typed(x=1,y=2,z=3)   #Typed(x=1,y=2,x=3)---> @deco ---->Foo=deco(Foo)
class Foo:
    pass
print(Foo.__dict__)

# @deco
@Typed(name='egon')
class Bar:
    pass
print(Bar.name)
View Code

装饰器=高阶函数+函数嵌套+闭包

@timer  #@timer就等同于cal=timer(cal)
def cal(array):
    res=0
    for i in array:
        res+=i
    return res

cal(range(10))
'''
闭包:在一个作用域里放入定义变量,相当于打了一个包
'''
def father(name):
    def son():
        # name='alex'
        print('我爸爸是 [%s]' %name)
        def grandson():
            # name='wupeiqi'
            print('我爷爷是 [%s]' %name)
        grandson()
    son()

father('XXX')
class Room:
    tag="mmp"
    def __init__(self,name,owner,width,length,heigh):
        self.name=name
        self.owner=owner
        self.width=width
        self.length=length
        self.heigh=heigh

    @property
    def cal_area(self):
        # print('%s 住的 %s 总面积是%s' %(self.owner,self.name,self.width*self.length))
        return self.width*self.length
    @property
    def cal_tiji(self):
        return self.length*self.width*self.heigh


    def test(cls):
        print('from test',self.name)

    @classmethod  # 专门供类使用的方法
    def tell_info(cls):
        print(cls)
        print('----->',cls.tag)


r1=Room('厕所','av',100,100,10000)
r2=Room('公共厕所','abc',10,10,10)

Room.tell_info()  #有特殊意义,类在调用自己函数属性是跟实例绑定在一起


print(Room.tag)
# print('%s 住的 %s 总面积是%s' %(r1.owner,r1.name,r1.width*r1.length))

print(r1.cal_area)
print(r2.cal_area)
print(r1.cal_tiji)
print(r1.name)
print(r2.name)
View Code
class Room:
    tag="mmp"
    def __init__(self,name,owner,width,length,heigh):
        self.name=name
        self.owner=owner
        self.width=width
        self.length=length
        self.heigh=heigh

    @property  #属性方法
    def cal_area(self):
        # print('%s 住的 %s 总面积是%s' %(self.owner,self.name,self.width*self.length))
        return self.width*self.length
    @property
    def cal_tiji(self):
        return self.length*self.width*self.heigh


    @classmethod  # 类方法,专门供类使用的方法
    def tell_info(cls,x):
        print(cls)
        print('----->',cls.tag,x)

    # def test(x,y):
    #     print(x,y)


    @staticmethod   #静态方法,类的从工具包
    def wash_body(a,b,c):
        print('%s %s %s 洗澡',(a,b,c))

Room.wash_body('alex','yuanhao','wupenqi')


r1=Room('厕所','alex',100,100,10000)
View Code

五、属性方法

class List(list):
    def append(self, object):
        if type(object)  is str:
            # self.append(object)
            # list.append(self,object)
            super().append(object)
        else:
            print('只能添加字符串')
    def show_midlle(self):
        mid_index=int(len(self)/2)
        return  self[mid_index]

l1=List('helloworld')
l1.append('SB')
print(l1)
View Code
class Chinese:
    country='China'
    def __init__(self,name):
        self.name=name

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

p1=Chinese('alex')
print(p1.__dict__)

#查看
print(p1.name)
p1.play_ball('篮球')

#增加

p1.age=18
print(p1.__dict__)
print(p1.age)

def test(self):
    print('实例函数属性')
p1.test=test
print(p1.__dict__)
p1.test(p1)

# #不要修改底层的属性字典
# p1.__dict__['sex']='male'
# print(p1.__dict__)
# print(p1.sex)

#修改
p1.age=19
print(p1.__dict__)
print(p1.age)

#删除
del p1.age
print(p1.__dict__)
View Code
class Chinese:
    contry='China'
    dang='gongchandang'
    def __init__(self,name):
        self.name=name

    def play_ball(self,ball):
        print('%s 正在打 %s'%(self,name))
#查看
print(Chinese.contry)

#修改
Chinese.contry='japan'
print(Chinese.contry)
p1=Chinese('alex')
print(p1.__dict__)
print(p1.contry)

#删除
del Chinese.dang
del Chinese.contry
print(Chinese.__dict__)

#增加
def eat_food(self,food):
    Chinese.eat=eat_food()
    print(Chinese.__dict__)

    p1.eat('aaa')
View Code
内置方法:
__str__:在对象被打印时自动触发,然后将该绑定方法的返回值(必须是字符串类型)当做本次打印的结果
class People:
    def __init__(self,name,age):
        self.name=name
        self.age=age

    def __str__(self):
        return '<name:%s age:%s>' %(self.name,self.age)

obj1=People('egon',18)
obj2=People('lxx',38)

print(obj1) #print(obj1.__str__())
print(obj2) #print(obj2.__str__())

__del__:在对象被删除前自动触发, 在该方法内应该执行与该对象有关的系统资源的回收操作
class Foo:
    def __init__(self,filename,encoding='utf-8'):
        self.f=open(filename,'r',encoding=encoding)

    def __del__(self):
        # print('run.....')
        self.f.close()

obj=Foo()
del obj #obj.__del__()

#obj.__del__()


六、高阶函数、迭代器、生成器

高阶函数定义
1、函数接收的参数是一个函数名
2、函数的返回值是一个函数名
3、满足上述条件任意一个,都可称为高阶函数

import  time
def Foo():
    print('------')

def test(func):
    print(func)
    start_time=time.time()
    func()
    stop_time=time.time()
    print('func_runtime %s' %(stop_time-start_time))

Foo()
test(Foo)  #修改函数调用方式
def run():
    print('from_run')

run()

def test():
    yield 1 #暂停,入口
    print('from_run')
    yield 2
    print('from_run')

t=test() 产生生成器对象
#唤醒迭代器
t.__next__()
next(t)  # yield10
t.send('123')
age=10
res=True if age>10 else False
# if 形式三元表达式
l=['a' for i in range(10)]  #列表解析
g_l=('a' for i in range(10)) #生成器表达式
print(l)
def test():
    for i in range(4):
        yield 1

t=test()
class Foo:
    def __init__(self,n):
        self.n=n
    def __iter__(self):
        return self
    def __next__(self):
        if self.n>=100:
            raise StopIteration
        self.n+=1
        return self.n
# l = list('hello')
# for i in l:
    # print(i)


f1 = Foo(10)
for i in f1: #f1.__iter__()----------->iter(f1)
    print(i)
View Code

七、反射

反射是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。

isinstance(obj,cls)检查是否obj是否是类 cls 的对象

issubclass(sub, super)检查sub类是否是 super 类的派生类


python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)

四个可以实现自省的函数

hasattr(object,name)
判断object中有没有一个name字符串对应的方法或属性

getattr(object,name,default=None)
从对象中获取命名属性;getattr(x, 'y')等价于x.y。
当给出一个默认参数时,当属性没有给出时,返回该参数
不存在,在这种情况下就会引发异常。

setattr(x,y,v)
将给定对象上的命名属性设置为指定值。
setattr(x, 'y', v)等于' x.y = v

delattr(x,y)
从给定对象中删除命名属性。
delattr(x, 'y')等价于' del x.y'


class Foo:
     pass

class Bar(Foo):
    pass

obj=Bar()

 print(isinstance(obj,Bar))
print(isinstance([],list))

print(issubclass(Bar,Foo))



# 反射:指的是通过字符串来操作属性
class Foo:
    def __init__(self,name):
        self.name=name


obj=Foo('eg')


# hasattr()
# print(hasattr(obj,'name')) #'name' in obj.__dict__

# getattr()
# print(getattr(obj,'name')) #obj.__dict__['name']
# print(getattr(obj,'age')) #obj.__dict__['age']
# print(getattr(obj,'age',None)) #obj.__dict__['age']

# setattr()
# setattr(obj,'age',18) #obj.age=18
# setattr(obj,'name','EG') #obj.name='EG'
# print(obj.__dict__)

# delattr()
# delattr(obj,'name')# del obj.name
# print(obj.__dict__)
code

导入其他模块,利用反射查找该模块是否存在某个方法


反射的好处

好处一:

实现可插拔机制,可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,什么意思?即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能

好处二:

动态导入模块(基于反射当前模块成员)

module_abc=__import__('dic.a.b.c')#从字符串中提取文件名
# module_abc 拿到最顶层dic模块
module_abc.a.b.c

八、__setattr__,__delattr__,__getattr__

三者的用法演示:

class Foo:
    x=1
    def __init__(self,y):
        self.y=y

    def __getattr__(self, item):
        print('----> from getattr:你找的属性不存在')


    def __setattr__(self, key, value):
        print('----> from setattr')
        # self.key=value #这就无限递归了,你好好想想
        # self.__dict__[key]=value #应该使用它

    def __delattr__(self, item):
        print('----> from delattr')
        # del self.item #无限递归了
        self.__dict__.pop(item)

#__setattr__添加/修改属性会触发它的执行
f1=Foo(10)
print(f1.__dict__) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值
f1.z=3
print(f1.__dict__)

#__delattr__删除属性的时候会触发
f1.__dict__['a']=3#我们可以直接修改属性字典,来完成添加/修改属性的操作
del f1.a
print(f1.__dict__)

#__getattr__只有在使用调用属性且属性不存在的时候才会触发
f1.xxxxxx

授权

???? 授权是包装的一个特性, 包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能。其它的则保持原样。授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。

实现授权的关键点就是覆盖__getattr__方法

授权的方式主要在于__getattr__将没有定义的方法映射到真正的内建函数file里面
import time
class FileHandle:
    def __init__(self,filename,mode='r',encoding='utf-8'):
        self.file=open(filename,mode,encoding=encoding)
        self.mode=mode
        self.encoding=encoding
    def write(self,line):
        print('---------',line)
        t=time.strftime('%Y-%m-%d %X')
        self.file.write('%s %s' %(t,line))

    def __getattr__(self, item):
        # print(item,type(item))
        return getattr(self.file,item)

    # def read(self):
    #     pass


f1=FileHandle('a.txt','r+')
print(f1.__dict__)
print('——————>',f1.read) # 触发 getattr
# print(f1.write)
f1.write('11111111211111\n')
# f1.seek(0)
# sys_f=open('b.txt','w+')
#授权的方式主要在于__getattr__将没有定义的方法映射到真正的内建函数file里面
class List:
    def __init__(self,seq,permission=False):
        self.seq=seq
        self.permission=permission
    def clear(self):
        if not self.permission:
            raise PermissionError('not allow the operation')
        self.seq.clear()

    def __getattr__(self, item):
        return getattr(self.seq,item)

    def __str__(self):
        return str(self.seq)
l=List([1,2,3])
# l.clear() #此时没有权限,抛出异常


l.permission=True
print(l)
l.clear()
print(l)

#基于授权,获得insert方法
l.insert(0,-123)
print(l)

九、__getattribute__

回顾__getattr__

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

    def __getattr__(self, item):
        print('执行的是我')
        # return self.__dict__[item]

f1=Foo(10)
print(f1.x)
f1.xxxxxx #不存在的属性访问,触发__getattr__

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

    def __getattribute__(self, item):
        print('不管是否存在,我都会执行')

f1=Foo(10)
f1.x
f1.xxxxxx

__getattribute__

? 当__getattribute__与__getattr__同时存在,只会执行__getattrbute__,除非__getattribute__在执行过程中抛出异常AttributeError

二者同时出现

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

    def __getattr__(self, item):
        print('执行的是我')
        # return self.__dict__[item]
    def __getattribute__(self, item):
        print('不管是否存在,我都会执行')
        raise AttributeError('哈哈')

f1=Foo(10)
f1.x
f1.xxxxxx


十、描述符(__get__,__set__,__delete__)


1、 描述符是什么:

描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议。

__get__():调用一个属性时,触发
__set__():为一个属性赋值时,触发
__delete__():采用del删除属性时,触发

class Foo: #在python3中Foo是新式类,它实现了三种方法,这个类就被称作一个描述符
    def __get__(self, instance, owner):
        pass
    def __set__(self, instance, value):
        pass
    def __delete__(self, instance):
        pass

描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)

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='egon'
f1.name
del f1.name
#疑问:何时,何地,会触发这三个方法的执行

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

    def __set__(self, instance, value):
        print('---set方法')
        instance.__dict__['x']=value

    def __delete__(self, instance):
        print('---delete方法')

class Bar:
    x=Foo()#定义成另外一个类的类属性
    def __init__(self,n):
        self.x=n

b1=Bar(10)
print(b1.__dict__)


一 数据描述符:至少实现了__get__()和__set__()

class Foo:
   def __set__(self, instance, value):
      print('set')
   def __get__(self, instance, owner):
      print('get')

二 非数据描述符:没有实现__set__()

 class Foo:
     def __get__(self, instance, owner):
         print('get')


注意事项:
一 描述符本身应该定义成新式类,被代理的类也应该是新式类
二 必须把描述符定义成这个类的类属性,不能为定义到构造函数中
三 要严格遵循该优先级,优先级由高到底分别是

1.类属性
2.数据描述符
3.实例属性
4.非数据描述符
5.找不到的属性触发__getattr__()

#描述符Str
class Str:
    def __get__(self, instance, owner):
        print('Str调用')
    def __set__(self, instance, value):
        print('Str设置...')
    def __delete__(self, instance):
        print('Str删除...')

class People:
    name=Str()
    def __init__(self,name,age): #name被Str类代理,age被Int类代理,
        self.name=name
        self.age=age


#基于上面的演示,我们已经知道,在一个类中定义描述符它就是一个类属性,存在于类的属性字典中,而不是实例的属性字典

#那既然描述符被定义成了一个类属性,直接通过类名也一定可以调用吧,没错
People.name #恩,调用类属性name,本质就是在调用描述符Str,触发了__get__()

People.name='egon' #那赋值呢,我去,并没有触发__set__()
del People.name #赶紧试试del,我去,也没有触发__delete__()
#结论:描述符对类没有作用-------->傻逼到家的结论

'''
原因:描述符在使用时被定义成另外一个类的类属性,因而类属性比二次加工的描述符伪装而来的类属性有更高的优先级
People.name #恩,调用类属性name,找不到就去找描述符伪装的类属性name,触发了__get__()

People.name='egon' #那赋值呢,直接赋值了一个类属性,它拥有更高的优先级,相当于覆盖了描述符,肯定不会触发描述符的__set__()
del People.name #同上
'''

类属性>数据描述符
View Code
#描述符Str
class Str:
    def __get__(self, instance, owner):
        print('Str调用')
    def __set__(self, instance, value):
        print('Str设置...')
    def __delete__(self, instance):
        print('Str删除...')

class People:
    name=Str()
    def __init__(self,name,age): #name被Str类代理,age被Int类代理,
        self.name=name
        self.age=age


p1=People('egon',18)

#如果描述符是一个数据描述符(即有__get__又有__set__),那么p1.name的调用与赋值都是触发描述符的操作,于p1本身无关了,相当于覆盖了实例的属性
p1.name='egonnnnnn'
p1.name
print(p1.__dict__)#实例的属性字典中没有name,因为name是一个数据描述符,优先级高于实例属性,查看/赋值/删除都是跟描述符有关,与实例无关了
del p1.name

数据描述符>实例属性
View Code

描述符应用

python是弱类型语言,即参数的赋值没有类型限制,可以通过描述符机制来实现类型限制功能

class Type:
    def __init__(self,key,expected_type):
        self.key=key
        self.expected_type=expected_type

    def __get__(self, instance, owner):
        print('get_methods')
        # print('getinstance参数[%s]'%instance)
        # print('owner参数[%s]'%owner)
        return instance.__dict__[self.key]

    def __set__(self, instance, value):
        print('set_methods')
        # print('setinstance参数[%s]' % instance)
        # print('value参数[%s]' % value)
        if not isinstance(value,self.expected_type) :
            # print('你传入的类型不是字符串,错误')
            # return
            raise TypeError('你传入的不是字符串')
        instance.__dict__[self.key]=value

    def __delete__(self, instance):
        print('delete_methods')
        # print('instance_arg[%s]'%instance)
        instance.__dict__.pop(self.key)

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

p1=People('egon',18,33.3)
p1.name='alex'
print(p1.__dict__)
p1.age

总结:

描述符是可以实现大部分python类特性中的底层功能,包括@classmethod,@staticmethd,@property甚至是__slots__属性

描述父是很多高级库和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件.

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:
    name='linhaifeng'
    @ClassMethod # say_hi=ClassMethod(say_hi)
    def say_hi(cls):
        print('你好啊,帅哥 %s' %cls.name)

People.say_hi()

p1=People()
p1.say_hi()
#疑问,类方法如果有参数呢,好说,好说

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

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

class People:
    name='linhaifeng'
    @ClassMethod # say_hi=ClassMethod(say_hi)
    def say_hi(cls,msg):
        print('你好啊,帅哥 %s %s' %(cls.name,msg))

People.say_hi('你是那偷心的贼')

p1=People()
p1.say_hi('你是那偷心的贼')

自己做一个@classmethod
定制@ClassMethod
class StaticMethod:
    def __init__(self,func):
        self.func=func

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

class People:
    @StaticMethod# say_hi=StaticMethod(say_hi)
    def say_hi(x,y,z):
        print('------>',x,y,z)

People.say_hi(1,2,3)

p1=People()
p1.say_hi(4,5,6)

定制@StaticMethod

十一、item系列方法

class Foo:
    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)

#字典操作触发 item
#. 触发  attr
f1= Foo()
print(f1.__dict__)
f1['name']='egon'
f1['22']='2222'

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

    def __getitem__(self, item):
        print(self.__dict__[item])

    def __setitem__(self, key, value):
        self.__dict__[key]=value
    def __delitem__(self, key):
        print('del obj[key]时,我执行')
        self.__dict__.pop(key)
    def __delattr__(self, item):
        print('del obj.key时,我执行')
        self.__dict__.pop(item)

f1=Foo('sb')
f1['age']=18
f1['age1']=19
del f1.age1
del f1['age']
f1['name']='alx'
print(f1.__dict__)


十二、property

一个静态属性property本质就是实现了get,set,delete三种方法

#实现类型检测功能

#第一关:
class People:
    def __init__(self,name):
        self.name=name

    @property
    def name(self):
        return self.name

# p1=People('alex') #property自动实现了set和get方法属于数据描述符,比实例属性优先级高,所以你这面写会触发property内置的set,抛出异常


#第二关:修订版

class People:
    def __init__(self,name):
        self.name=name #实例化就触发property

    @property
    def name(self):
        # return self.name #无限递归
        print('get------>')
        return self.DouNiWan

    @name.setter
    def name(self,value):
        print('set------>')
        self.DouNiWan=value

    @name.deleter
    def name(self):
        print('delete------>')
        del self.DouNiWan

p1=People('alex') #self.name实际是存放到self.DouNiWan里
print(p1.name)
print(p1.name)
print(p1.name)
print(p1.__dict__)

p1.name='egon'
print(p1.__dict__)

del p1.name
print(p1.__dict__)


#第三关:加上类型检查
class People:
    def __init__(self,name):
        self.name=name #实例化就触发property

    @property
    def name(self):
        # return self.name #无限递归
        print('get------>')
        return self.DouNiWan

    @name.setter
    def name(self,value):
        print('set------>')
        if not isinstance(value,str):
            raise TypeError('必须是字符串类型')
        self.DouNiWan=value

    @name.deleter
    def name(self):
        print('delete------>')
        del self.DouNiWan

p1=People('alex') #self.name实际是存放到self.DouNiWan里
p1.name=1


十三、__str__,__repr__,__format__,__slots__

改变对象的字符串显示__str__,__repr__

自定制格式化字符串__format__

format_dict={
    'nat':'{obj.name}-{obj.addr}-{obj.type}',#学校名-学校地址-学校类型
    'tna':'{obj.type}:{obj.name}:{obj.addr}',#学校类型:学校名:学校地址
    'tan':'{obj.type}/{obj.addr}/{obj.name}',#学校类型/学校地址/学校名
}
class School:
    def __init__(self,name,addr,type):
        self.name=name
        self.addr=addr
        self.type=type

    def __repr__(self):
        return 'School(%s,%s)' %(self.name,self.addr)
    def __str__(self):
        return '(%s,%s)' %(self.name,self.addr)

    def __format__(self, format_spec):
        # if format_spec
        if not format_spec or format_spec not in format_dict:
            format_spec='nat'
        fmt=format_dict[format_spec]
        return fmt.format(obj=self)

s1=School('oldboy1','北京','私立')
print('from repr: ',repr(s1))
print('from str: ',str(s1))
print(s1)

'''
str函数或者print函数--->obj.__str__()
repr或者交互式解释器--->obj.__repr__()
如果__str__没有被定义,那么就会使用__repr__来代替输出
注意:这俩方法的返回值必须是字符串,否则抛出异常
'''
print(format(s1,'nat'))
print(format(s1,'tna'))
print(format(s1,'tan'))
print(format(s1,'asfdasdffd'))

1.__slots__是什么:

    是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)
    使用点.来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的)
2.为何使用__slots__:
    字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__
    当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给实例添加新的属性了,只能使用在__slots__中定义的那些属性名。
3.注意事项:
    __slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再 支持一些普通类特性了,比如多继承。大多数情况下,你应该只在那些经常被使用到的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象 。
关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷。更多的是用来作为一个内存优化工具。
class Foo:
    __slots__='x'


f1=Foo()
f1.x=1
f1.y=2#报错
print(f1.__slots__) #f1不再有__dict__

class Bar:
    __slots__=['x','y']

n=Bar()
n.x,n.y=1,2
n.z=3#报错
class Foo:
    __slots__=['name','age']

f1=Foo()
f1.name='alex'
f1.age=18
print(f1.__slots__)

f2=Foo()
f2.name='egon'
f2.age=19
print(f2.__slots__)

print(Foo.__dict__)
#f1与f2都没有属性字典__dict__了,统一归__slots__管,节省内存

十四、__next__、__iter__、__doc__

斐波那契数列

class Fib:
    def __init__(self):
        self._a=0
        self._b=1

    def __iter__(self):
        return self

    def __next__(self):
        self._a,self._b=self._b,self._a + self._b
        return self._a

f1=Fib()

print(f1.__next__())
print(next(f1))
print(next(f1))

for i in f1:
    if i > 100:
        break
    print('%s ' %i,end='')

class Foo:
    '我是描述信息'
    pass

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

十五、__module__、__class__、__del__

__module__ 表示当前操作的对象在那个模块

__class__???? 表示当前操作的对象的类是什么

from lib.aa import C

obj = C()
print obj.__module__  # 输出 lib.aa,即:输出模块
print obj.__class__      # 输出 lib.aa.C,即:输出类


__del__析构方法,当对象在内存中被释放时,自动触发执行。
注:如果产生的对象仅仅只是python程序级别的(用户级),那么无需定义__del__,如果产生的对象的同时还会向操作系统发起系统调用,即一个对象有用户级与内核级两种资源,比如(打开一个文件,创建一个数据库链接),则必须在清除对象的同时回收系统资源,这就用到了__del__

典型的应用场景:

创建数据库类,用该类实例化出数据库链接对象,对象本身是存放于用户空间内存中,而链接则是由操作系统管理的,存放于内核空间内存中

当程序结束时,python只会回收自己的内存空间,即用户态内存,而操作系统的资源则没有被回收,这就需要我们定制__del__,在对象被删除前向操作系统发起关闭数据库链接的系统调用,回收资源

f=open('a.txt') #做了两件事,在用户空间拿到一个f变量,在操作系统内核空间打开一个文件
del f #只回收用户空间的f,操作系统的文件还处于打开状态

#所以我们应该在del f之前保证f.close()执行,即便是没有del,程序执行完毕也会自动del清理资源,于是文件操作的正确用法应该是
f=open('a.txt')
读写...
f.close()
很多情况下大家都容易忽略f.close,这就用到了with上下文管理

十六、__enter__、__exit__、__call__

在操作文件对象的时候

with open('a.txt') as f:
   '代码块'

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

上下文管理协议:

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

    def __enter__(self):
        print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
        # return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with中代码块执行完毕时执行我啊')


with Open('a.txt') as f:
    print('=====>执行代码块')
    # print(f,f.name)

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

如果__exit()返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行

class Open:
    def __init__(self,filepath,mode='r',encoding='utf-8'):
        self.filepath=filepath
        self.mode=mode
        self.encoding=encoding

    def __enter__(self):
        # print('enter')
        self.f=open(self.filepath,mode=self.mode,encoding=self.encoding)
        return self.f

    def __exit__(self, exc_type, exc_val, exc_tb):
        # print('exit')
        self.f.close()
        return True
    def __getattr__(self, item):
        return getattr(self.f,item)

with Open('a.txt','w') as f:
    print(f)
    f.write('aaaaaa')#授权
    f.wasdf #抛出异常,交给__exit__处理

用途:

1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预

2.在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处

对象后面加括号,触发执行 __call__

注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()

class Foo:

    def __init__(self):
        pass

    def __call__(self, *args, **kwargs):

        print('__call__')


obj = Foo() # 执行 __init__
obj()       # 执行 __call__

十七、元类 metaclass

python中一切皆为对象。

所有的对象都是实例化或者说调用类而得到的(调用类的过程称为类的实例化)

类的产生过程其实就是元类的调用过程


class关键字在帮我们创建类时,调用了元类type(...),那调用type时传入类的关键组成部分,一个类有三大组成部分,分别是

1、类名class_name='xxxx'

2、基类们class_bases=(object,)

3、类的名称空间class_dic,类的名称空间是执行类体代码而得到的

调用type时会依次传入以上三个参数


一个类没有声明自己的元类,默认他的元类就是type,除了使用内置元类type,我们也可以通过继承type来自定义元类,然后使用metaclass关键字参数为一个类指定元类,用class自定义的类也全都是对象(包括object类本身也是元类type的 一个实例,可以用type(object)查看)




原文链接:https://www.cnblogs.com/bind/p/11516938.html

文章标签: