python 笔记 03—python高级语法


浅拷贝、深拷贝

import copy

a= [11,22]
b = a  # 赋值
c = copy.copy()   # 浅拷贝
d = copy.deepcopy(a)  # 深拷贝
a.append(33) # c值还是[11,22]

等号赋值、浅拷贝、深拷贝之间的区别:

1、等号赋值等同于拷贝原数据的指向(字符,列表,元组,字典),改变原数据时,赋值后的变量指向的值也会一起变

2、浅拷贝只拷贝指向,不拷贝数据,例:拷贝列表,但是,拷贝之后,原列表数据修改,新增导致指向变化,拷贝指向的数据跟原数据会产生差异

3、深拷贝整个拷贝原数据+指向,但是无法拷贝整个元组(即里外整体全是元组的数据),所以原数据改变后,新拷贝的值不变

4、如果元组内部有可变的量(例如:([11,22],[33,44])),深拷贝可以正常拷贝,否则,深拷贝也不会拷贝只能正常指向原元组

深拷贝的应用场景:需要保留原数据,保证原数据不变,用深拷贝可以拷贝一份随意改动

import导入模块:

import导入会依次检查sys.path路径下是否有这个模块,找不到则报错:

>>> from pprint import pprint
>>> pprint(sys.path)
['',
'D:\\workspace\\virtualenv\\py38django2\\Scripts\\python38.zip',
'D:\\workspace\\virtualenv\\py38django2\\DLLs',
'D:\\workspace\\virtualenv\\py38django2\\lib',
'D:\\workspace\\virtualenv\\py38django2\\Scripts',
'c:\\python38\\Lib',
'c:\\python38\\DLLs',
'D:\\workspace\\virtualenv\\py38django2']

  重新导入:

import aa
print(aa.test_aa())
# 如果此时修改test_aa()里代码,想要新代码生效,不能直接import 需要:
reload(aa)
print(aa.test_aa())

封装、继承、多态: 

class T(object):
    num = 0
    def __init__(self, age):
        self.age = age

t = T(11)
print(t.__dict__)
print(t.__class__)

打印结果:

{'age': 11}

 继承各个父子类之间的属性关系:

class Parent(object):
    x = 1

class Child1(Parent):
    pass

class Chlid2(Parent):
    pass

print(Parent.x, Chlid1.x, Child2.x)
Chlid1.x = 2  # 这步会给当前Chlid1类添加个类属性 x 并赋值
print(Parent.x, Chlid1.x, Child2.x)
Parent.x = 3
print(Parent.x, Chlid1.x, Child2.x)
# 结果:
# 1, 1, 1
# 1, 2, 1
# 3, 2, 3
# 结论:注意继承不会复制父类里的属性,调用子类里没有的属性值时,回去父类找

 多态,看一则案例:

class MiniOS(object):
    """多态案例-模拟操作系统安装应用"""

    def __init__(self, name):
        self.name = name
        self.apps = []

    def __str__(self):
        return "{}安装的应用列表有为{}".format(self.name, self.apps)

    def add_app(self, name):
        self.apps.append(name)

    def install_apps(self, app):
        # 判断是否已经安装了软件
        if app.name in self.apps:
            print("已经安装了{},无需再次安装。".format(app.name))
        else:
            app.install()
            self.add_app(app.name)
            print("{}已经完成安装!".format(app.name))


class App(object):
    def __init__(self, name, version, desc):
        self.name = name
        self.version = version
        self.desc = desc

    def __str__(self):
        return "应用:{},版本:{},描述:{}".format(self.name, self.version, self.desc)

    def install(self):
        print("将{}{}安装到制定目录中...".format(self.name, self.version))


class PyCharm(App):
    pass


class Chrome(App):
    def install(self):
        print("解压{}安装包".format(self.name))
        super().install()


linux = MiniOS("linux")
pycharm = App("pycharm", "2019.3", "绿色免费版")
chrome = App("chrome", "73.0.1", "用于selenium测试")
linux.install_apps(pycharm)
linux.install_apps(chrome)
linux.install_apps(chrome)

结果:

将pycharm2019.3安装到制定目录中...
pycharm已经完成安装!
将chrome73.0.1安装到制定目录中...
chrome已经完成安装!
已经安装了chrome,无需再次安装。

多继承(MRO顺序):

class Parent(object):
    def __init__(self, name, *args, **kwargs):   # 防止调用报错,使用不定长参数,接收参数
        """父类"""
        print("父类开始初始化")
        self.name = name
        print("父类结束初始化")


class Son1(Parent):
    def __init__(self, name, age, *args, **kwargs):
        """子类1"""
        print("Son1开始初始化")
        self.age = age
        super().__init__(name, *args, **kwargs)
        print("Son1结束初始化")


class Son2(Parent):
    def __init__(self, name, gender, *args, **kwargs):
        """子类2"""
        print("Son2开始初始化")
        self.gender = gender
        super().__init__(name, *args, **kwargs)
        print("Son2结束初始化")


class Grandson(Son2, Son1):
    def __init__(self, name, age, gender):
        """子类2"""
        print("Grandson开始初始化")
        #  super(Son1,self).__init(name, age, gender) 这种写法可以任意指定方法,然后从此方法往后执行
        super().__init__(name, age, gender)
        print("Grandson结束初始化")


# 通过调用C3算法,查看超类调用链(元组)
print(Grandson.__mro__)

 *args,**kwargs用法

# 方法定义时使用
def test(a,b,*args,**kwargs):
    print(a)
    print(b)
    print(args)
    print(kwargs)

test(11,22,33,44,55,66,name="Tom",age=2)
# 结果:
# args =  (33,44,55,66)
# kwargs = {"name":"Tom", "age":"2"}

# 函数调用时使用
list1 = [33,44,55,66]
dict1 = {"name":"Jerry","age":"3"}
test(11,22,*list1,**dict1)
# 结果:
# args =  (33,44,55,66)
# kwargs = {"name":"Jerry", "age":"3"}

 实例方法、类方法、静态方法:

class Foo(object):
    class_str = 11

    # 类对象只能有一个,实例对象可以有多个
    def __init__(self, name):
        self.name = name

    def ord_func(self):
        """实例方法,self指向实例对象,可以修改实例属性self.name"""
        print(self.name)
        print("实例方法!")
        self.class_func()
        self.static_func()

    @classmethod
    def class_func(cls):
        """类方法,cls指向类对象,可以修改类属性class_str"""
        cls.class_str = 22
        print("类方法!")
        # cls.ord_func()  # 类方法不能调实例方法
        cls.static_func()

    @staticmethod
    def static_func():
        """静态方法,内部调用时直接写方法,外部调用时无需实例化"""
        print("静态方法!")


# 外部调用除实例方法以外,
f = Foo("Annis")
f.ord_func()
f.class_func()   # 或者 Foo.class_func()
f.static_func()  # 或者 Foo.static_func()

 property属性装饰器

用法,上代码:

class Goods(object):

    @property
    def size(self):     # 不能传参数
        return self._size

    @size.setter
    def size(self, value):
        if value > 0:
            self._size = value
        else:
            raise ValueError("尺寸不合格!")

  @size.deleter
def size(self):
     del self._size

goods = Goods()
size = goods.size
goods.size = -1 # 无法赋值
del goods.size # 删除属性值

作用跟意义:可以用调用属性的方式直接调用方法,获得返回值,如果直接把属性暴露出去,虽然写起来很简单,但是没办法按预期设想参数检查(比如:成绩不能 <0、>100,年龄不能<0,性别不能为男,女之外的值)

property另外一种写法:

class API(object):

    def _get_post(self):
        return self._post

    def _set_post(self, value):
        if value > 0:
            self._post = value
        else:
            raise ValueError("")

    def _del_post(self):
        del self._post

    POST = property(_get_post, _set_post, _del_post)

内建属性:

常用专有属性说明触发方式
__init__ 构造初始化函数 创建实例后,赋值时使用,在__new__
__new__ 生成实例所需属性 创建实例时

__class__

实例所在的类 实例.__class__

__module__

实例所在的模块,即py文件 实例.__module__
__str__ 实例字符串表示,可读性 print(类实例),如没实现,使用repr结果
__repr__ 实例字符串表示,准确性 类实例 回车 或者 print(repr(类实例))
__del__ 析构 del删除实例
__dict__ 实例自定义属性 vars(实例.__dict__)
__doc__ 类文档,子类不继承,类的描述(类名下面那行注释) help(类或实例)
__getattribute__ 属性访问拦截器 访问实例属性时
__bases__ 类的所有父类构成元素 类名.__bases__
__call__ 类里实现__call__方法,然后用实例()调用 实例()

__getitem__

__setitem__

__delitem__

类里实现这些方法,然后调用,类似get set del

obj = F()

r = obj['k1']  # 触发get

obj['k2'] = '老王' # 触发set

del obj['k1']  # 触发del

with、上下文管理器:

with存在的意义:

def m1():
    f = open("a.txt", "wb")
    f.write("222")
    f.close()


def m2():
    f = open("a.txt", "wb")
    try:
        f.write("222")
    except Exception as e:
        raise e
    finally:
        f.close()


def m3():
    with open("a.txt", "wb") as f:
        f.write("aaa")
# 遇到异常会自动关闭open打开的文件

 with实现原理:

class File(object):
    def __init__(self, filename, mode):
        self.filename = filename
        self.mode = mode

    def __enter__(self):
        print("entering")
        self.f = open(self.filename, self.mode)
        return self.f

    def __exit__(self, *args):
        print("will exit")
        self.f.close()


with File("hh.txt", "w") as f:
    f.write('xxxx')

# 所有实现了 __enter__,__exit__的程序也可以使用with

另外一种实现方法:

from contextlib import contextmanager

@contextmanager
def my_open(path, mode):
    f = open(path, mode)
    yield f
    f.close()

# 调用with一样的效果
with my_open('out.txt', 'w') as f:
    f.write("hello")

装饰器-元编程

实例:

from functools import wraps


def print_result(func):
    @wraps(func)   # 不改变使用装饰器原有函数的结构(如name, doc)
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        print(result)
        return result

    return wrapper


@print_result
def add(x, y):
    return x + y


add(5, 3)