美文网首页
三、Python高级

三、Python高级

作者: Dante617 | 来源:发表于2021-06-21 22:23 被阅读0次

1 Python里的拷贝

1.1 顶层对象为不可变数据类型

1.1.1 子元素都为不可变数据类型

import copy
a = ((1, 2), (3, 4))
b = a
c = copy.copy(a)
d = copy.deepcopy(a)

1.1.2 子元素存在可变数据类型

import copy
a = ((1, 2), [3, 4])
b = a
c = copy.copy(a)
d = copy.deepcopy(a)

1.2 顶层对象为可变数据类型

1.2.1 子元素都为不可变数据类型

import copy
a = [(1, 2), (3, 4)]
b = a
c = copy.copy(a)
d = copy.deepcopy(a)

1.2.2 子元素存在可变数据类型

import copy
a = [(1, 2), [3, 4]]
b = a
c = copy.copy(a)
d = copy.deepcopy(a)

2 闭包

2.1 匿名函数、函数、闭包、对象当做实参时的区别

  • 匿名函数能够完成基本的简单功能,传递的是这个函数的引用(只有功能)
  • 函数能够完成较为复杂的功能,传递的是这个函数的引用(只有功能)
  • 闭包能够完成较为复杂的功能,传递的是这个闭包中的函数以及数据(功能+数据),相比对象占用极少空间
  • 对象封装较为复杂的数据和功能,传递很多数据和很多功能(功能+数据)

2.2 闭包形成的条件

当一个内嵌函数引用其外部作用域的变量,我们就会得到一个闭包. 总结一下,创建一个闭包必须满足以下几点:

  • 必须有一个内嵌函数
  • 内嵌函数必须引用外部函数中的变量
  • 外部函数的返回值必须是内嵌函数

2.3 闭包简单使用

def config_name(name):
    def inner(msg):
        print(name + ':' + msg)
    return inner

tom = config_name('tom')  # name = 'tom'会被保存在tom这个函数引用中,只需要赋值一次
jerry = config_name('jerry')  # name = 'jerry'会被保存在jerry这个函数引用中,只需要赋值一次

tom('你好')  # tom:你好
jerry('Hello')  # jerry:Hello
tom('我叫tom')  # tom:我叫tom
jerry('我叫jerry')  # jerry:我叫jerry

2.4 闭包内层函数修改外层函数的变量

如果闭包内层函数需要修改外层函数的变量,要用nonlocal关键字,如下:

def warp():
    num = 1
    def inner():
        nonlocal num 
        print(num )
        num  = 2
        print(num )
    return inner

inner()()
# 1
# 2

如果去掉上述代码中nonlocal num语句,那么程序在print(num)处就会报错,因为解释器会把内层inner函数中num = 2看成是定义的一个局部变量,而在定义局部变量num前打印其值会报变量未定义的错误,此处需留意

3 装饰器

3.1 简单装饰器

def decorator(fun):
    def wrapper():
        print("方法执行前")
        fun()
        print("方法执行后")
    return wrapper

@decorator
# 相当于my_fun = decorator(my_fun)
def my_fun():
    print("需要装饰的函数")

my_fun()
# 输出:
# 方法执行前
# 需要装饰的函数
# 方法执行后

3.2 通用装饰器

def decorator(fun):
    def wrapper(*args, **kwargs):
        print("方法执行前")
        result = fun(*args, **kwargs)
        print("方法执行后")
        return result
    return wrapper

@decorator
def my_fun1():
    print("需要装饰的函数")

@decorator
def my_fun2(a, b):
    return a + b

my_fun1()
# 输出:
# 方法执行前
# 需要装饰的函数
# 方法执行后
print(my_fun2(1, 2))
# 输出:
# 方法执行前
# 方法执行后
# 3

3.3 多个装饰器

多个装饰器装饰同一个函数时,由下向上一层层装饰

def make_div(fun):
    def wrapper():
        return '<div>' + fun() + '</div>'
    return wrapper

def make_p(fun):
    def wrapper():
        return '<p>' + fun() + '</p>'
    return wrapper

@make_div
@make_p
def content():
    return "内容"

print(content())  # <div><p>内容</p></div>

3.4 带参数的装饰器

def decorator_out(flag):
    def decorator_in(fun):
        def wrapper(a, b):
            if flag == "+":
                print("执行加法操作")
            else:
                print("执行减法操作")
            fun(a, b)
        return wrapper
    return decorator_in

@decorator_out("+")
# 相当于两步
# temp = decorator_out("+")
# my_fun = temp(my_fun)
def add(a, b):
    print(a + b)

add(1, 2)
# 输出
# 执行加法操作
# 3

3.5 类装饰器

class MyDecorator:
    def __init__(self, fun):
        self.__fun = fun

    def __call__(self, *args, **kwargs):
    # 类的call方法允许实例化对象进行自身调用,python中每一个函数都实现了call方法
        print('函数执行前')
        self.__fun()
        print('函数执行后')

@MyDecorator
# 相当于my_fun = MyDecorator(my_fun)
def my_fun():
    print("需要装饰的函数")

my_fun()
# 输出:
# 方法执行前
# 需要装饰的函数
# 方法执行后

装饰器会在程序编译时就开始装饰,所有装饰器都会执行装饰操作,而不是在函数调用时才去装饰,不受函数调用的影响

4 super().方法名()与类名.方法名()的区别

4.1 类名.方法名()

class Parent:
    def __init__(self, name):
        print("Parent init start")
        self.name = name
        print("Parent init end")

class Son1(Parent):
    def __init__(self, name, age):
        print("Son1 init start")
        self.age = age
        Parent.__init__(self, name)
        print("Son1 init end")

class Son2(Parent):
    def __init__(self, name, gender):
        print("Son2 init start")
        self.gender = gender
        Parent.__init__(self, name)
        print("Son2 init end")

class GrandSon(Son1, Son2):
    def __init__(self, name, age, gender):
        print("GrandSon init start")
        Son1.__init__(self, name, age)
        Son2.__init__(self, name, gender)
        print("GrandSon init end")

grand_son = GrandSon('zhangsan', 7, 0)

# 控制台输出如下:
# GrandSon init start
# Son1 init start
# Parent init start
# Parent init end
# Son1 init end
# Son2 init start
# Parent init start
# Parent init end
# Son2 init end
# GrandSon init end

导致Parent中__init__方法被调用两次

4.2 super().方法名()

class Parent:
    def __init__(self, name, *args, **kwargs):
        print("Parent init start")
        self.name = name
        print("Parent init end")

class Son1(Parent):
    def __init__(self, name, age, *args, **kwargs):
        print("Son1 init start")
        self.age = age
        super().__init__(name, *args, **kwargs)  # super()代表Son2
        print("Son1 init end")

class Son2(Parent):
    def __init__(self, name, gender, *args, **kwargs):
        print("Son2 init start")
        self.gender = gender
        super().__init__(name, *args, **kwargs)  # super()代表Parent
        print("Son2 init end")

class GrandSon(Son1, Son2):
    def __init__(self, name, age, gender):
        print("GrandSon init start")
        super().__init__(name, age, gender)  # super()代表Son1
        print("GrandSon init end")

print(GrandSon.__mro__)
grand_son = GrandSon('zhangsan', 7, 0)

# 控制台输出如下:
# (<class '__main__.GrandSon'>, <class '__main__.Son1'>, <class '__main__.Son2'>, <class '__main__.Parent'>, <class 'object'>)

# GrandSon init start
# Son1 init start
# Son2 init start
# Parent init start
# Parent init end
# Son2 init end
# Son1 init end
# GrandSon init end

super()在复杂的多继承中不是简单的指向父类,而是指向调用类的__mro__元组中该类的下一个类,Python3中MRO元组顺序为按继承关系自左向右广度遍历

5 property属性

5.1 装饰器方式

class Student:
    def __init__(self):
        self.__age = 0

    @property
    def age(self):
        print("获取年龄")
        return self.__age

    @age.setter
    def age(self, value):  # 函数名要与property装饰的函数名保持一致
        print("设置年龄")
        self.__age = value

student = Student()
print(student.age)
student.age = 200
print(student.age)
# 控制台输出:
# 获取年龄
# 0
# 设置年龄
# 获取年龄
# 200

5.2 类属性方式

class Student:
    def __init__(self):
        self.__age = 0

    def get_age(self):
        print("获取年龄")
        return self.__age

    def set_age(self, value):
        print("设置年龄")
        self.__age = value

    age = property(get_age, set_age)

student = Student()
print(student.age)
student.age = 200
print(student.age)
# 控制台输出:
# 获取年龄
# 0
# 设置年龄
# 获取年龄
# 200

6 上下文管理器

6.1 with语句

with open("test.txt", "w") as f:
    f.write("hello")

with语句既简单又安全,并且with语句执行完毕之后会自动调用关闭文件操作,即使其代码块中出现异常也会自动调用关闭文件操作。with语句之所以这么强大,是因为其背后是由上下文管理器做支撑的

6.2 自定义上下文管理器(类方式)

一个类只要实现了__enter____exit__这两个方法,通过该类创建的对象我们就称之为上下文管理器

class File:
    def __init__(self, name, model):
        self.name = name
        self.model = model

    def __enter__(self):
        print("上文方法")
        self.file = open(self.name, self.model)
        return self.file

    def __exit__(self, exc_type, exc_val, exc_tb):
        print("下文方法")
        self.file.close()

with File("test.txt", "r") as f:
    # print(f.read())
    f.write("test")
# 控制台输出:
# 上文方法
# 下文方法
# 报错信息io.UnsupportedOperation: not writable

上述程序是在test.txt文件存在的情况下执行报错(没有写权限),上下文管理器即使发生异常也会执行__exit__方法,如果test.txt文件不存在,则执行报错(没有找到文件),这种异常不会执行__exit__方法

6.3 自定义上下文管理器(装饰器方式)

from contextlib import contextmanager

@contextmanager
def my_open(name, model):
    try:
        print("上文方法")
        file = open(name, model)
        yield file
    except Exception as e:
        print(e)
    finally:
        print("下文方法")
        file.close()

with my_open("test.txt", "r") as f:
    # print(f.read())
    f.write("test")
# 控制台输出:
# 上文方法
# not writable
# 下文方法

上述程序无论test.txt文件是否存在,都会执行__exit__方法,但test.txt文件不存在时,程序执行最后还会报UnboundLocalError: local variable 'file' referenced before assignment的错

7 生成器

生成器根据规则循环生成数据,数据不是一次性全部生成,而是使用一个生成一个,可以节省大量内存

7.1 简单生成器

my_generator = (i ** 2 for i in range(3))
print(my_generator)  # <generator object <genexpr> at 0x000002012773CB48>

# 遍历方法一
while True:
    try:
        print(next(my_generator))
    except:  # 当next方法取到生成器最后一个元素,继续使用next方法取值会报错
        break

# 遍历方法二
for i in my_generator:
    print(i)

7.2 复杂生成器

def my_generator():
    for i in range(3):
        print("开始生成数据")
        yield i  # 当程序执行的yield时,程序暂停并把结果返回,再次启动生成器时会在暂停的位置继续向下执行
        print("上一次数据生成完毕")

result = my_generator()
print(result)  # <generator object my_generator at 0x000001347AE17AC8>

print(next(result))
# 控制台输出:
# 开始生成数据
# 0

print(next(result))
# 控制台输出:
# 上一次数据生成完毕
# 开始生成数据
# 1

遍历方法同简单生成器

7.3 生成器使用场景

def fibonacci(num):
    a = 0
    b = 1
    current_index = 0
    while current_index < num:
        result = a
        a, b = b, a + b
        current_index += 1
        yield result
        
fib = fibonacci(5)
for i in fib:
    print(i)

8 单例模式

8.1 使用__new__方法

class Singleton(object):
    _instance = None
    def __new__(cls, *args, **kw):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

singleton = Singleton()

8.2 共享属性

创建实例时把所有实例的__dict__指向同一个字典,这样它们具有相同的属性和方法.

class Borg:
    _state = {}
    def __new__(cls, *args, **kw):
        ob = super().__new__(cls)
        ob.__dict__ = cls._state
        return ob

borg = Borg()

8.3 装饰器版本

def singleton(cls):
    instance = None
    def getinstance(*args, **kwargs):
        nonlocal instance  # 修改外部装饰器函数中变量instance,注意不能使用global
        if instance is None:
            instance = cls(*args, **kwargs)
        return instance
    return getinstance

@singleton
class MyClass:
  ...

8.4 import方法

作为python的模块是天然的单例模式

# mysingleton.py
class My_Singleton:
    def foo(self):
        pass

my_singleton = My_Singleton()

# to use
from mysingleton import my_singleton

my_singleton.foo()

9 元类(type)

元类(type)用来动态创建类,类用来动态实例化对象,type使用方法如下:

type(类名, 由父类名称组成的元组(针对继承的情况,可以为空),包含属性的字典(属性和方法))

9.1 简单使用

class A:
    def print_num(self):
        print(self.num)

def __init__(self):
    self.num = 100

@staticmethod
def print_static():
    print("static method")

@classmethod
def print_class(cls):
    print(cls.attr)


B = type("B", (A,), {"attr": "class_attr", "__init__": __init__, "print_static": print_static, "print_class": print_class})
b = B()
b.print_num()  # 100
b.print_static()  # static method
b.print_class()  # class_attr
print(B.__mro__)  # (<class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
print(B.__class__)  # <class 'type'>
print(b.__class__)  # <class '__main__.B'>

9.2 元类使用场景

9.2.1 函数方式

def upper_attr(class_name, class_parents, class_attr):
    new_attr = {}
    for name, value in class_attr.items():
        if not name.startswith("__"):
            new_attr[name.upper()] = value
    return type(class_name, class_parents, new_attr)

class Foo(object, metaclass=upper_attr):  # 或class Foo(metaclass=upper_attr):
    bar = "bip"

print(hasattr(Foo, "bar"))  # False
print(hasattr(Foo, "BAR"))  # True

f = Foo()
print(f.BAR)  # bip

9.2.2 类方式

class UpperAttrMetaClass(type):
    def __new__(cls, class_name, class_parents, class_attr):
        new_attr = {}
        for name, value in class_attr.items():
            if not name.startswith("__"):
                new_attr[name.upper()] = value

        # 方法一:通过type来做类对象的创建
        return type(class_name, class_parents, new_attr)

        # 方法二:复用type.__new__方法
        # return type.__new__(cls, class_name, class_parents, new_attr)

class Foo(metaclass=UpperAttrMetaClass):
    bar = "bip"

print(hasattr(Foo, "bar"))  # False
print(hasattr(Foo, "BAR"))  # True

f = Foo()
print(f.BAR)  # bip

函数定义好可以使用装饰器为函数增加功能,类定义好可以使用元类为类增加功能

相关文章

网友评论

      本文标题:三、Python高级

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