python基础

python基础

1. 闭包

1. 概念

有时候需要在函数外部得到函数内的局部变量,于是在函数的内部,再定义一个函数,把f2作为返回值,result就相当于f2。

def f1():
    n=999
    def f2():
        print(n)
    
    return f2

result = f1()
result()

上一部分代码中的f2函数,就是闭包。

nonlocal语句
使用nonlocal语句可以让嵌套函数的内层函数去修改外层函数的变量,但是不影响全局变量
1)修改嵌套函数内部的global变量
在这里插入图片描述
2)修改嵌套函数内部的nonlocal变量
在这里插入图片描述

2. 类和对象

对象=属性+方法
所谓属性就是类里面的变量,所谓方法就是类里面的函数

(1)类的创建

先创建一个类:相当于模具,再根据模具进行批量生产
在这里插入图片描述

t1 = Turtle()
#t1就是turtle类的对象,也叫实例对象,它就拥有了这个类的属性和方法
t1.legs = 3#修改属性
t1.mouth = 1#添加属性

(2)面向对象的三个基本特性:封装、继承、多态

1)封装

在创建对象之前,通过创建类,将相关的变量和属性打包到一起。
类中的每一个方法默认的第一个参数都是self(传递给方法的是实例对象自身),因为一个类有无数个方法,需要确定是哪个对象在调用
在这里插入图片描述

2)继承

python的类可以继承,它可以使用现有类的所有功能,并在无需重新编写代码的情况下,对这些功能进行扩展。通过继承创建的新类叫子类。

class A:
    x = 520
    def hello(self):
        print("你好")
        
#B继承A的写法
class B(A):
    pass#表示占位的空语句
b = B()#b可以访问到类A中的属性和方法

#类B中重新定义变量和方法,可以实现覆盖,B的对象结果更新
class B(A):
    x = 111
    def hello(self):
        print("why")
      
#判断一个对象是否属于某个类,可以使用isinstance函数
isinstance(b,B)
isinstance(b,A)
#b属于子类B,也同时属于父类A

issubclass(B,A)#检测B是否为A的子类

多重继承
一个子类可以同时继承多个父类

class A:
    x = 520
    def hello(self):
        print("A")
class B:
    x = 111
    y = 222
    def hello(self):
        print("B")
class C(A,B):
    pass
c = C()#520
c.hello()#A
#访问顺序从左到右

组合

class Dog:
    def say(self):
        print("汪汪汪")
class Cat:
    def say(self):
        print("喵喵喵")  
class Zoo:
    d = Dog()
    c = Cat()
    def say(self):
        self.d.say()#方法是大家的,属性可以是自己的,所以用self来绑定对象z和类Zoo
        self.c.say()#没有self就表示一个局部变量而不是z的属性
z = Zoo()
z.say()
#汪汪汪
#喵喵喵

想知道对象拥有哪些属性,可以用__dict__进行内省

d = Dog()
d.x = 5
d.__dict__#{'x': 5}

通过空类生成的实例来模拟字典

#定义一个最小的类
class C:
	pass
#可以作为字典使用
C.x = 250;
C.y = "abc"
C.z = [1,2,3]
print(C.x)
print(C.y)
print(C.z)
#对比使用字典的方法
d = {}
d['x'] = 250
d['y'] = "abc"
d['z'] = [1,2,3]
print(d['x'])
print(d['y'])
print(d['z'])
#通过空类生成的实例来模拟字典
class C:
	pass
c = C()
c.x = 250;
c.y = "abc"
c.z = [1,2,3]

构造函数__init__
在实例化对象的同时实现个性化定制,可以传递参数

class C:
    def __init__(self,x,y):
        self.x = x
        self.y = y
    def add(self):
        return self.x + self.y
    def mul(self):
        return self.x * self.y
c = C(2,3)
c.add()#5
c.mul()#6

重写

class D(C):
    def __init__(self,x,y,z):
        C.__init__(self,x,y)#直接通过类名访问类里面的方法称为调用未绑定的父类方法
        self.z = z
    def add(self):
        return C.add(self) + self.z
    def mul(self):
        return C.mul(self) * self.z
d = D(2,3,4)
d.add()#9
d.mul()#24

修改钻石继承问题
在这里插入图片描述

class A():
    def __init__(self):
        print("A")
class B1(A):
    def __init__(self):
        A.__init__(self)#调用未绑定的父类A的init方法
        print("B1")     
class B2(A):
    def __init__(self):
        A.__init__(self)
        print("B2")        
class C(B1,B2):
    def __init__(self):
        B1.__init__(self)
        B2.__init__(self)
        print("C")   
c = C()
#A
#B1
#A
#B2
#C
#修改后
class B1(A):
    def __init__(self):
        super().__init__()#使用super函数去查找父类(不一定是父类,而是MRO列表中下一个类)的方法,括号里不再需要self
        print("B1")     
class B2(A):
    def __init__(self):
        super().__init__()
        print("B2")        
class C(B1,B2):
    def __init__(self):
        super().__init__()
        print("C")   
#A
#B2
#B1
#C

MRO顺序:类的方法解析顺序

C.mro()
#[__main__.C, __main__.B1, __main__.B2, __main__.A, object]
C.__mro__
#[__main__.C, __main__.B1, __main__.B2, __main__.A, object]

多继承中的Mixin

class Animal:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def say(self):
        print(f"我叫{self.name},今年{self.age}岁。")
class FlyMixin:
    def fly(self):
        print("I can fly.")
class Pig(Animal):
    def special(self):
        print("pig")
        
#FlyMixin()类怎么插入到Pig类里面呢?
#通过多继承的方式写到Pig的父类里面
class Pig(FlyMixin,Animal):
    def special(self):
        print("pig")
#然后就可以调用FlyMixin()里面的方法p.fly()了
3)多态

同一个运算符、函数或对象在不同的场景下具有不同的作用效果

#Square,Circle,Triangle都继承自同一个类,但是重写了area的计算方法
class Shape:
    def __init__(self,name):
        self.name = name
    def area(self):
        pass
    
class Square(Shape):
    def __init__(self,length):
        super().__init__("正方形")
        self.length = length
    def area(self):#重写计算面积的方法
        return self.length * self.length
    
class Circle(Shape):
    def __init__(self,r):
        super().__init__("圆形")
        self.r = r
    def area(self):#重写计算面积的方法
        return 3.14 * self.r * self.r   
    
class Triangle(Shape):
    def __init__(self,a,h):
        super().__init__("三角形")
        self.a = a
        self.h = h
    def area(self):#重写计算面积的方法
        return 0.5 * self.a * self.h   
s = Square(5)
c = Circle(6)
t = Triangle(3,4)
s.area()#25
c.area()#113.03999999999999
t.area()#6.0

自定义函数如何实现多态接口

class Cat:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def intro(self):
        print(f"我叫{self.name},今年{self.age}岁。")
    def say(self):
        print("mua")
class Dog:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def intro(self):
        print(f"我叫{self.name},今年{self.age}岁。")
    def say(self):
        print("汪汪")
class Pig:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def intro(self):
        print(f"我叫{self.name},今年{self.age}岁。")
    def say(self):
        print("oink")

c = Cat("web",4)
d = Dog("bubu",7)
p = Pig("大肠",5)

def animal(x):#animal函数就具有多态性,接收不同的对象作为参数,并且不检查类型的情况下执行它的方法
    x.intro()
    x.say()
    
animal(c)#我叫web,今年4岁。mua
animal(d)#我叫bubu,今年7岁。汪汪
animal(p)#我叫大肠,今年5岁。oink

鸭子类型
animal函数不需要关注x是什么,只要x里面有intro和say方法,就可以被调用

(3)“私有变量”

通过某种手段,使得对象中的属性或方法无法被外部所访问。name mangling(名字改编)

class C:
    def __init__(self,x):
        self.__x = x#__x使得变量__x私有,c.__x无法访问到x的值
    def set_x(self,x):
        self.__x = x
    def get_x(self):
        print(self.__x)
c = C(250)
#可以使用函数访问
c.set_x(100)
c.get_x()
#或者使用_C__x来访问,不建议那么做,因为使用__就是为了不让外界访问到
c.__dict__#{'_C__x': 100}
c._C__x#100

方法的隐藏

class D:
    def __fun(self):
        print("hello")

对象创建之后再使用__x将不能进行名字改编,如c.__y=250,__y依然可以被外部访问。名字改编是发生在类实例化对象的时候。
_单个下横线开头的变量通常是仅供内部使用的变量
单个下横线结尾的变量通常是要用关键字做变量名,如class

(4)效率提升之道

__slot__类属性,避免了利用字典来存放造成空间上的浪费,以牺牲Python语言的动态灵活性为前提
如果一个类的对象只需要几个固定的属性,也没有动态添加属性的需求,可以用__slot__类属性

class C:
    __slots__=["x","y"]
    def __init__(self,x):
    self.__x = x
c = C(250)
#c只能访问x,y,c.z=666报错
#构造函数中也不能有xy以外的属性,__init__(self,x,y,z)报错

继承自父类的slots属性不会在子类中生效,C的子类对象e可以添加z属性

(5)对象的魔法方法

哪一个魔法方法创建了实例化对象/实例化对象中调用的第一个魔法方法?
对象被创建时调用的魔法方法 new(cls[,…])
需要重写__new__()的情况极少,两种情况:1、在元类中去定制类 2、在继承不可变数据类型的时候,想要改变数据类型

class CapStr(str):
    def __new__(cls,string):#第一个参数式cls,第二个参数是实例化传递进去的参数
        string = string.upper()#之所以能对不可变对象进行修改,是因为在实例对象被创建之前就进行了拦截
        return super().__new__(cls,string)#然后才调用super()去创建真正的实例
cs = CapStr("abc")
cs#ABC小写的输入变成了大写 
#CapStr类继承自str字符串类,字符串的方法也一并继承了下来
cs.lower()#变成小写abc
cs.capitalize()#首字母大写Abc

对象即将被销毁时也会调用一个魔法方法def del(self):

#对象即将被销毁时也会调用一个魔法方法__del__(self)
class C:
    def __init__(self):
        print("我来了")
    def __del__(self):
        print("我走了")
c = C()#我来了#实例化对象自动调用init方法
del c#我走了#del c之后调用__del__方法
#但是调用del C不一定会触发魔法方法,只有对象被销毁时才能触发,当检测到一个对象没有任何引用时会被销毁
d = c#当d还在引用c时
del c#不会调用__del__方法,没有输出
del d#此时再del 引用c的d,输出我走了

对象的重生

#利用全局变量
class D:
    def __init__(self,name):
        self.name = name
    def __del__(self):
        global x#通过创建全局变量把self给送出去
        x = self
d = D("xiao")
d.name#xiao
del d#删除d之后d访问不到了,但是x可以访问到,x就是原来的d

#对象被销毁前创建一个函数,通过参数传递将self传递出去
class E:
    def __init__(self,name,func):#实例化时先把参数给传进去
        self.name = name
        self.func = func
    def __del__(self):
        self.func(self)#在对象被删除之前将self传递给一个函数
        #通过闭包将外层函数的局部变量保存下来
def outter():
    x = 0#定义闭包,让self保存在外部函数的变量中
    def inner(y=None):
        nonlocal x#内部函数窃取self对象
        if y:
            x = y
        else: 
            return x
    return inner
f = outter()
e = E("a",f)
del e#调用del,将self存储起来
g = f()#g就是刚才e这个对象

(5)与运算相关的魔法方法

重新定义add方法,变为字符统计
在这里插入图片描述
radd方法,s1的add方法要返回NotImplemented
调用radd方法时,两者要基于不同的类
在这里插入图片描述
增强赋值运算,会改编s1的数据类型,对s1进行赋值
在这里插入图片描述

(6)与属性访问相关的魔法方法

hasatter(c,“name”)#检测某对象是否有某属性
getattr(c,“name”)#获取对象中的某个属性
setattr(c,“name”,“123”)#设置对象中指定属性的值
delattr(c,“name”)#删除对象中的某个属性

class C:
    def __init__(self,name,age):
        self.name = name
        self.__age = age
    def __getattribute__(self,attrname):
        print("拿来吧你")
        return super().__getattribute__(attrname)
c = C("xiao",18)
getattr(c,"name")
#拿来吧你
#'xiao
class C:
    def __init__(self,name,age):
        self.name = name
        self.__age = age
    def __getattribute__(self,attrname):
        print("拿来吧你")
        return super().__getattribute__(attrname)
    def __getattr__(self,attrname):#只有在访问不存在的属性时才会拦截
        if attrname == "fish":
            print("I love fish.")
        else:
            raise AttributeError(attrname)
c = C("xiao",18)
c.fish#拿来吧你 I love fish.
c.x#拿来吧你
class D:
    def __setattr__(self,name,value):
        self.__dict__[name] = value#注意要使用字典访问
d = D()
d.name = "fish"
d.name#fish
class D:
    def __setattr__(self,name,value):
        self.__dict__[name] = value
    def __delattr__(self,name):
        del self.__dict__[name]
d = D()
d.name = "fish"
d.__dict__#{'name': 'fish'}
del d.name
d.__dict__#{}

(7)索引、切片、迭代协议

当对象被索引时,python会调用 getitem(self,index)方法,既能响应单个下标的索引操作,又能支持代表范围的切片索引。
在这里插入图片描述
为索引或切片赋值时,会被__setitem__拦截

class D:
    def __init__(self,data):
        self.data = data
    def __getitem__(self,index):
        return self.data[index] 
    def __setitem__(self,index,value):
        self.data[index] = value
d = D([1,2,3,4,5])
d[1]#2
d[2:4]#[3, 4]
d[2:4]=[2,3]
d[:]#[1, 2, 2, 3, 5]
class D:
    def __init__(self,data):
        self.data = data
    def __getitem__(self,index):
        return self.data[index] * 2
d = D([1,2,3,4,5])
for i in d:
    print(i,end = ' ')#2 4 6 8 10

针对可迭代对象的魔法方法__iter__(self),next(self)

#针对可迭代对象的魔法方法__iter__(self),__next__(self)
#根据python的迭代协议,如果一个对象定义了__iter__(self)方法,那它就是可迭代对象,如果一个对象定义了__next__(next)方法,那它就是迭代器
#创造一个迭代器对象
class Double:
    def __init__(self,start,stop):
        self.value = start - 1
        self.stop = stop
    def __iter__(self):
        return self
    def __next__(self):
        if self.value == self.stop:
            raise StopIteration
        self.value += 1
        return self.value * 2

d = Double(1,5)
for i in d:
    print(i,end = ' ')#2 4 6 8 10

(8)代偿

用于实现成员关系的检测 contains(self,item),对应运算符in/not in

class C:
    def __init__(self,data):
        self.data = data
    def __contains__(self,item):
        print("hi")
        return item in self.data
    
c = C([1,2,3,4,5])
3 in c#hi True
#如果没有使用__contains__(self,item),但是又用了in/not in进行成员关系判断,Python就会尝试去查找__iter__(self),__next__(self)方法
class C:
    def __init__(self,data):
        self.data = data
    def __iter__(self):
        print("Iter",end = "->")
        self.i = 0
        return self
    def __next__(self):
        print("Next",end = "->")
        if self.i == len(self.data):
            raise StopIteration
        item = self.data[self.i]
        self.i += 1
        return item

c = C([1,2,3,4,5])
3 in c#Iter->Next->Next->Next->True
6 in c#Iter->Next->Next->Next->Next->Next->Next->False

#如果__iter__(self),__next__(self)方法都没有,则会查找__getitem__()方法
class C:
    def __init__(self,data):
        self.data = data
    def __getitem__(self,index):
        print("Getitem",end = "->")
        return self.data[index]
        c = C([1,2,3,4,5])
3 in c#Getitem->Getitem->Getitem->True
6 in c#Getitem->Getitem->Getitem->Getitem->Getitem->Getitem->False

布尔测试

#遇到bool()函数,python会去寻找__bool__()方法
class D:
    def __bool__(self):
        print("Bool")
        return True
d = D()
bool(d)#Bool True

#如果没有定义__bool__()方法,Python就会去寻找__len__(),返回非0,表示true,返回0,表示False
class D:
    def __init__(self,data):
        self.data = data
    def __len__(self):
        print("Len")
        return len(self.data)

d = D("abc")
bool(d)#Len True

跟运算相关的魔法方法
在这里插入图片描述

#定义比较运算符判断字符串长度
class S(str):
    def __lt__(self,other):
        return len(self) < len(other)
    def __gt__(self,other):
        return len(self) > len(other)
    def __eq__(self,other):
        return len(self) == len(other)
    #若不想让某个方法生效,只需在后面加
    #__lt__ = None
    #__gt__ = None
    #__eq__ = None
        s1 = S("abc")
s2 = S("edc")
s1 < s2#False
s1 == s2#True

(9)像调用一个函数一样去调用对象

#像调用一个函数一样去调用对象,需要对象的类去定义__call__()方法
class C:
    def __call__(self):
        print("hi")
c = C()
c()#hi

#call支持位置参数、关键字参数
class C:
    def __call__(self,*args,**kwargs):#*代表位置参数,**代表关键字参数
        print(f"位置参数->{args}\n关键字参数->{kwargs}")
c = C()
c(1,2,3,x=250,y=520)
#位置参数->(1, 2, 3)
#关键字参数->{'x': 250, 'y': 520}
class Power:
    def __init__(self,exp):
        self.exp = exp
    def __call__(self,base):
        return base ** self.exp
square = Power(2)
square(2)#4
square(5)#25
cube = Power(3)
cube(2)#8
cube(5)#125

跟字符串相关的魔法方法

#跟字符串相关的魔法方法__str__(self)参数转化为字符串,__repr__(self)将对象转化为程序可执行的字符串
#repr可以作为str的代偿,反过来的话不行

在这里插入图片描述
repr可以打印整个列表,str不可以
在这里插入图片描述
在这里插入图片描述
通过同时定义两个方法,可以让对象在不同场景下有不同的显示
在这里插入图片描述

(10)property函数

#class property(fget=None,fset=None,fdel=None,doc=None)#本质上是一个内置的类
class C:
    def __init__(self):
        self._x=250
    def getx(self):
        return self._x
    def setx(self,value):
        self._x=value
    def delx(self):
        del self._x
    x = property(getx,setx,delx) 
c = C()
c.x#250
c.x = 300
c.__dict__#{'_x': 300}
del c.x
c.__dict__#{}
#将property函数作为装饰器使用,创建只读属性
class E:
    def __init__(self):
        self._x=250
    @property#将内置函数作为装饰器来使用x=property(x)
    def x(self):
        return self._x
e = E()
e.x#250
e.x = 520#e.x是只读的,所以会报错
#多个参数传入
class E:
    def __init__(self):
        self._x=250
    @property#将内置函数作为装饰器来使用x=property(x)
    def x(self):
        return self._x
    @x.setter
    def x(self,value):
        self._x=value
    @x.deleter
    def x(self):
        del self._x
e = E()
e.x#250
e.x = 300
e.__dict__#{'_x': 300}
del e.x
e.__dict__#{}

(11)类方法和静态方法

类方法

专门用来绑定类的方法@classmethod

class C:
    def funA(self):
        print(self)
    @classmethod
    def funB(cls):#类方法
        print(cls)
c = C()
c.funA()#普通方法,绑定的是一个对象object
#<__main__.C object at 0x000002024E383A20>
c.funB()#绑定的类<class '__main__.C'>
#通过创建列表来维护一个类对应的所有对象
class C:
    count = 0
    def __init__(self):
        C.count += 1
    @classmethod
    def get_count(cls):
        print(f"该类一共实例化了{cls.count}个对象")
c1 = C()
c2 = C()
c3 = C()
c3.get_count()#该类一共实例化了3个对象

静态方法

放在类里面的函数,不需要和对象绑定

class C:
    @staticmethod
    def funC():
        print("123")
c = C()
c.funC()#既可通过对象访问,又可通过类访问
C.funC()
#用静态方法统计对象数量
class C:
    count = 0
    def __init__(self):
        C.count += 1
    @staticmethod
    def get_count():
        print(f"该类一共实例化了{C.count}个对象")
c1 = C()
c2 = C()
c3 = C()
c3.get_count()#该类一共实例化了3个对象
class C:
    count = 0
    def __init__(self):
        C.count += 1
    @classmethod#添加另一个类方法
    def add(cls):
        cls.count += 1#对应类属性的count
    def __init__(self):
            self.add()#让构造函数去调用add类方法,哪个类去调就会传入哪个类
    @classmethod
    def get_count(cls):
        print(f"该类一共实例化了{cls.count}个对象")
class D(C):
    count = 0
class E(C):
    count = 0
c1 = C()
d1,d2 = D(),D()
e1,e2,e3 = E(),E(),E()
c1.get_count()#该类一共实例化了1个对象
d1.get_count()#该类一共实例化了2个对象
e1.get_count()#该类一共实例化了3个对象

(12)描述符

描述符协议:实现了get,set,delete三个中任何一个或多个方法的类
在这里插入图片描述
在这里插入图片描述
self参数对应描述符类D的实例对象
instance对应被描述符拦截的属性所在的类的对象,类C的对象c
owner对应被描述符拦截的属性所在的类

#将property引用的案例修改为描述符形式
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值