Python基础

1.数据类型:

# 可变:

list ,dict ,set(集合)

# 不可变:

int bool str tuple

# 有序: list,tuple,str,int,bool # 无序: dict(python3.6以后可以是有序的了),set


# 取值方式: # 索引: str list tuple # 直接: set ,int ,bool # 键: dict

 

注意:'is'和'=='

  '=='只比较值是否一样

  'is'不仅比较值一样还要比较内存地址是否一样

2.深浅拷贝:

import copy

lst1=[1,2,3]

lst2=copy.copy(lst1)#浅拷贝

lst3=copy.deepcopy(lst1)#深拷贝

lst4=lst1 #赋值

 

定义:

浅拷贝 :

浅拷贝只拷贝第一层(如果有列表嵌套,他会复制那一个索引位置的列表的内存地址,而那个列表内的数据不会复制,说白了就是共用内部的list,最外层的list是自己新建的)

 

深拷贝:

可变类型拷贝,不可变类型共用(列表嵌套时,内层的列表地址不会复制,因为list是可变的,他是自己创个内部的list,然后共用list内的不可变元素的地址)

其实深拷贝不可变元素时也应该重新创建内存,来访对应的不可变元素,因为小数据池的关系所以就没新开辟地址.

 

注:

  如果发生了拷贝,内存地址一定发生变化,说白了拷贝就是自己新建地址,放复制过来的内容.

 

如下图:深浅拷贝如果只拷贝一个单一的列表(列表内不包含字典列表等可变元素)的情况下,深浅拷贝效果都一样:

 再看代码:

#如果只是单一列表,深浅拷贝都一样
import copy

lst1=[1,2,3]

lst2=copy.copy(lst1)#浅拷贝

lst3=copy.deepcopy(lst1)#深拷贝

print(id(lst1),id(lst2),id(lst3))#列表地址新建,全都不一样
# 44954968 44957408 44990712
print(id(lst1[1]) , id(lst2[1]) , id(lst3[1]))#内部不可变元素全都共用
# 496100128 496100128 496100128

 

 

 

 重点(这里要着重理解):

列表内部加了列表后(深浅拷贝就不一样了):

 

看代码:

#如果列表套列表,深浅拷贝大不一样了
import copy

lst1=[1,2,3,[4,5,6,[7,8,9]]]

lst2=copy.copy(lst1)#浅拷贝

lst3=copy.deepcopy(lst1)#深拷贝

print(id(lst1),id(lst2),id(lst3))#地址新建,全都不一样
# 44954968 44957408 44990712
print(id(lst1[1]) , id(lst2[1]) , id(lst3[1]))#内部不可变元素全都共用
# 496100128 496100128 496100128

# 看内部列表
print(id(lst1[3]) , id(lst2[3]) , id(lst3[3]))#深拷贝是自己建的所以不一样,浅拷贝是共用的
# 52295000 52295000 52330984

# 看内部列表的内部元素
print(id(lst1[3][0]) , id(lst2[3][0]) , id(lst3[3][0]))#4是不可变元素,共用
# 496296768 496296768 496296768

# 看内部列表再套的列表
print(id(lst1[3][3]) , id(lst2[3][3]) , id(lst3[3][3]))#还是只有深拷贝自己建(因为列表不管在哪都是可变元素),浅拷贝共用
# 44758360 44758360 44796584

print(id(lst1[3][3][1]) , id(lst2[3][3][1]) , id(lst3[3][3][1]))#里面的不可变元素8还是共用的
# 493675392 493675392 493675392

 

 

特殊情况(元组套列表):

如果单一元组深浅拷贝就都一样:

import copy

v1=(1,2,3)

v2=copy.copy(v1)#浅拷贝

v3=copy.deepcopy(v1)#深拷贝

print(id(v1[1]),id(v2[1]),id(v3[1]))#内部不可变元素都是一样的
# 501474080 501474080 501474080

print(id(v1),id(v2),id(v3))#浅拷贝只是单一元组,深浅拷贝都一样,因为里面都是不可变元素,元组也不可变
# 47061912 47061912 47061912
copy.copy(v)=copy.deepcopy(v)
#单一元组时深浅拷贝都一样

 

如果元组里有列表:

import copy

v1=(1,2,3,[4,5,6])

v2=copy.copy(v1)#浅拷贝

v3=copy.deepcopy(v1)#深拷贝

print(id(v1),id(v2),id(v3))#浅拷贝不变,深拷贝会变,因为里面多了不可变元素,深拷贝要变的
# 18215728 18215728 48191024
print(id(v1[1]),id(v2[1]),id(v3[1]))#内部不可变元素都是一样的
# 501474080 501474080 501474080

print(id(v1[3]),id(v2[3]),id(v3[3]))#内部列表已经不同地址了所以元组也变了
# 51377496 51377496 51413360
元组套列表(元组里面都是不可变元素,都不变,怎么拷贝都一样,如果元组里面有可变元素,例如列表,此时深拷贝会变,浅拷贝没事,和原来一样) 浅拷贝不变 深拷贝变了,因为深拷贝中不可变元素不会共用

 

3.闭包:

# 闭包,为函数创建一块区域(开辟一个新的内存地址),
# 并且能保护这个函数内部的变量或者是这个函数内部创建的新函数不被销毁,
# 并能一直为这个函数的执行提供这些被保护的数据

name ='oldboy'#这是全局变量,自己函数内部使用变量时,没有的话先找父级,父级没有才会找全局

def bar(name):
    #name='alex' 这样定义一个变量和你把'alex'当参数传进来是一个意思
    def inner():
        print(name)#使用变量名时,先找自己,自己没有就找父级,父级没有就找全局
    return inner #注意这里返回的只是函数名,而不是函数名+括号

v1=bar('alex') #{name='alex',inner}当函数执行时会自动开辟一块空间(内存地址)来执行这个函数,
# 执行之后就会销毁这个执行函数的内存,而闭包不会销毁,这块执行函数的内存一直存在,这里的name变量和inner函数就被保护了起来,一直存在.
到了这里相当于是v1=inner,这个函数内部的变量一直在被调用,所以不会销毁.除非v1执行了(inner函数被执行后)就销毁了
v2=bar('eric') #{name='eric',inner} #v1和v2开辟的是不同的内存地址,并不会共用,所以他俩互相不影响 v1()#输出'alex' 此时v1的bar函数新开辟的执行内存就销毁了 v2()#输出'eric' 此时v2的bar函数新开辟的执行内存也被销毁了

 

闭包练习:

#第一题:

info=[] def func(): print(item) for item in range(10): #循环不创建作用域,所以他还是在全局范围做循环,直接循环到9就停止,并返回给info里面10个func函数名和全局变量item=9 info.append(func) info[0]()

#输出:9

#第二题:
info=[]
def func(i):
def inner():
print(i)
return inner
for item in range(10): #先循环,每次循环的结果都被添加到info里面去,0,1,2...9
info.append(func(item)) #这执行了func函数,并把inner返回给了info列表,而每次的item都不一样

info[0]() #输出0

info[1]() #输出1
info[6]() #输出6
 

 

高阶函数:

  ①对函数名进行赋值  v=func

  ②把函数名当作参数传递  def xx(ww):    xx(func)

  ③把函数名当做返回值返回    return func

 

4.装饰器: 

普通装饰器:

定义:

在不改变原函数内部代码的基础上,在函数执行之前和函数执行之后添加某些功能
#装饰器,习惯上将装饰器函数命名为wrapper,这里用的是func,内部函数习惯命名为inner
def func(arg):
    def inner():
        print('before')
        res=arg()
        print('after')
        return res
    return inner

#被装饰器装饰的函数@func是简写,原来是
# v=func(index)
# index=v
# 或者直接写成:index=func(index)
@func
def index():
    print('123')
    return '666'

print(index)
# 此时输出为:<function func.<locals>.inner at 0x03A51150>,表示index现在
# 是func函数中的一个内部函数inner,index不再是以前的index函数了,原来的index函数已经传进去赋值给了arg了

print(index())#在执行原来的index函数之前会输出before,结束后会输出after,
# 而index的返回值一定是在after之后再输出,注意看代码的执行顺序
# 输出:
# before
# 123
# after
# 666

# 装饰器的功能:
# 第一步:执行func函数(就是装饰器函数),并且将被装饰的函数的函数名当做参数传递到装饰器中,相当于:func(index)
# 第二步:将func函数的返回值inner赋值给被装饰器装饰的函数的函数名(此时这个函数名已经不能调用原来被装饰的函数了,而是inner函数了).这一步操作相当于 index=func(index)

 

格式:

装饰器的编写格式:

def 外层函数(参数):
    def 内层函数(*args,**kwargs):
        return 参数(*args,**kwargs)
    return 内层函数
装饰器应用格式: @外层函数
def index(): pass index()

内层函数用
*args,**kwargs接收参数:
这样你的装饰器可以接收没有参数的函数,有一个参数或者多个参数的装饰器都行.

 

 

带参数的装饰器:

带参数的装饰器本质就是在原来的装饰器外边再加个函数.

因为原来的装饰器中@只能写一个参数,就是装饰器的函数名,所以用带参数的装饰器来增加你需要的参数,就是在执行装饰器之前,先执行一个函数,用这个函数把参数传到装饰其中(例如在装饰其中执行n次被装饰的函数,这个n就得是带参数的装饰器传进去的n)

#执行n次被装饰的函数
def x(n):
    print('x函数')
    def wrapper(arg):
        print('wrapper函数')
        def  inner(*args,**kwargs):
            print('inner函数')
            res=''
            for i in range(n):
                res = arg()
                print('这是执行第(',i+1,')次啦!')
            return res
        return inner
    return wrapper

@x(9)#传的参数就是9
def index():
    print('123')
    return '666'

print(index())


#只返回第n次执行的返回值
def x(n):
    print('x函数')
    def wrapper(arg):
        print('wrapper函数')
        def  inner(*args,**kwargs):
            print('inner函数')
            for i in range(n):
                res = arg()
            return res
        return inner
    return wrapper
@x(9)
def index():
    print('123')
    return '666'

print(index())

 

多个装饰器装饰一个函数:

这里常考的其实就是执行顺序.把执行顺序看明白了就好了.(顺序就是一条龙,跟考试贴座位号是一个道理)

一个函数正上方有多个装饰器时,先执行离被装饰函数最近的装饰器(如下图,执行顺序一定是3,2,1)

 

但是执行的位置要注意

  ①外层函数(wrapper内的代码)顺序分别是3-2-1,此时一条龙走到了1

  ②内层函数(inner函数内,到被装饰函数f之前的代码),1-2-3,此时一条龙走到了3

  ③执行被装饰的函数f,此时还在3

  ④执行inner函数中被装饰函数下面的代码,3-2-1

 

def wrapper1(f):
    print("我是wrapper1")
    def inner1():
        print("我是wrapper1的开始")
        f()
        print("我是wrapper1的结束")
    return inner1
def wrapper2(f):         # f=func()
    print("我是wrapper2")
    def inner2():
        print("我是wrapper2的开始")
        f()
        print("我是wrapper2的结束")
    return inner2

def wrapper3(f):
    print("我是wrapper3")
    def inner3():
        print("我是wrapper3的开始")
        f()
        print("我是wrapper3的结束")
    return inner3
@wrapper1
@wrapper2
@wrapper3
def func():
    print('我是被装饰的函数!')
func()

输出:
我是wrapper3
我是wrapper2
我是wrapper1
我是wrapper1的开始
我是wrapper2的开始
我是wrapper3的开始
我是被装饰的函数!
我是wrapper3的结束
我是wrapper2的结束
我是wrapper1的结束

@wrapper1拆开:func=wrapper1(func) wrapper1装饰的是前两个wrapper装饰过的inner
@wrapper2拆开:func=wrapper2(func)   wrapper2装饰的其实是wrapper3装饰过的inner
@wrapper3拆开:func=wrapper3(func)   wrapper3装饰的其实就是内层的inner

 

5.单例模式

定义:单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类无论实例化多少次都只有一个对象实例。

白话定义:一个类,实例化多少次出来的对象都是一个,就像赋值一样,一个实例化对象,但是可以有多个名字,但是他们(这些名字)都指一个实例化对象.

 

实例化对象干了三件事:

  1. 类名加()自动执行object类中的__new__方法,产生并返回一个对象空间

  1. 执行__init__方法,将对象空间传给self.

  1. __init__方法中给对象封装基础属性

 

应用场景:Django的admin.py和settings文件

  ①admin.py:每个应用都有一个admin.py.但是修改每一个admin的内容后,所有的应用的admin内容都会加载,出来,证明他们都用的一个admin.py

    ②settings.py:是因为源码中把settings文件内容都写到一个类中,实例化对象,再调用对象,from xxx import settings文件第一次会把settings文件的内容加载到内存中,后面无论from xxx import settings多少次都是直接在内存中直接拿,而不是再次实例化对象,随意每次用的

settings文件的内容都是一个对象出来的内容.

 

 

正规版单例模式:(单例模式的类最好都用singleton做为类名,singleton就是单例模式的意思)

#正规版单例模式(new+锁+线程)import threading
#类名都用singleton,是单例的意思
class Singleton(object):
        instance = None
        lock = threading.RLock()  #锁放类里面当属性
        
        def __new__(cls, *arg, **kwargs):
                if cls.instance:
                        return cls.instance
                with cls.lock:             #用类去调用锁
                        if not cls.instance:
                                cls.instance = object.__new__(cls)
                        return cls.instance
                
def task():
    obj = Singleton()
    print(obj)

for i in range(10):
    t = threading.Thread(target=task)
    t.start()

 

面试版单例模式:

 

#面试时可以去掉线程(new+锁)锁一定要有
import threading

class Singleton(object):
        instance = None
        lock = threading.RLock()
        
        def __new__(cls, *args, **kwargs):
                if cls.instance:
                        return cls.instance
                with cls.lock:
                        if not cls.instance:
                                cls.instance = object.__new__(cls)#这里object中传的参数cls就是Singleton类,如果把cls换成Singleton也是可以的
                        return cls.instance
                
obj1 = Singleton()
obj2 = Singleton()

 

文件版单例模式:

#调用文件时也是单例模式:
#在xx.py文件中:
class Singleton(object):
    def __init__(self):
        self.names = []

    def fun(self):
        pass
site = Singleton()


在另一个与其相邻的www.py文件中:
import xx    #第一次import会从文件中读取,放到内存中

print(xx.site)


import xx #第二次import会直接从内存中拿,而不再是用import读文件,所以就是单例模式,无论用多少次import都是第一次读的内容
print(xx.site)

 

错误版单例模式:

class Singleton(object):  
        instance = None

        def __init__(self):
                self.name = None

        def __new__(cls, *arg, **kawrgs):
                if not cls.instance:
                        cls.instance = object.__new__(cls)
                return cls.instance

这个单例模式有问题,每次实例化对象,都会在init把上一个对象的内容全部清空,再写内容,所以,这个单例模式不能给里面传参数,只能实例化对象,没有其他属性添加进去.

 

posted @ 2020-04-30 12:56  圣君灬七夜  阅读(268)  评论(0编辑  收藏  举报