pythonDay05 核心编程Part1(模块、作用域、深拷贝、浅拷贝、私有化、property、动态语言、生成器、迭代器、闭包、装饰器)

目录

0x00 重新导入模块:

0x01 循环导入

0x02 作用域:

0x03 == 、is:

 

0x04 深拷贝 和 浅拷贝 (重点)

0x05 私有化

0x06 property 的使用

0x04python是动态语言

那么如何给对象添加方法呢?

0x04生成器

0x05 迭代器

0x06 闭包

0x07 装饰器


import 在导入模块时,我们如何知道import在哪个路径下去寻找这个模块呢?先import sys,然后输入sys.path 就可以查看各个路径的优先顺序。sys.path.append("路径")  加入一个路径。import 导入一个模块时,会首先看当前路径下有没有这个模块,然后按照sys.path列表中顺序逐个查看列表中的路径 有没有这个模块。

0x00 重新导入模块:

当你在程序中导入了一个模块后,该模块被修改了,如何才能使用修改后的新模块呢?这是需要使用reload(模块)重新导入模块。reload 函数在 imp模块中,需要先导入imp模块

import test
test.test()
from imp import *
reload(test)
test.test()

0x01 循环导入

a.py

from b import b

def a():
    print("aaaaaaa")
    b()

b.py

from a import a

def b():
    print("----bb----")

def c():
    print("----c-----")
    a()

我们需要从设计者的角度上来防止这种情况的发生。

0x02 作用域:

在ipython3中输入globals,打印当前命名空间中所有的全局变量

def test():
    a = 100
    b = 200
    print(locals()) #打印该函数中所有的局部变量(名值对 封装在一个字典中)
    #{'a':100,'b':200}

注意区分 python中局部变量的作用域 和 C++中局部变量的作用域的不同,C++中一个变量的作用域 是 它所直接属于的那个大括号,所括住的范围。因此在C++中for循环,while循环 中定义的变量都是局部变量 。而python中没有大括号这个概念,因此在python中for循环 和 while循环 中定义的变量 都是全局变量! 其余部分,例如 在函数中定义的变量,python和C++基本相同,即都认为是局部变量。

0x03 == 、is:

== 判断两个变量的值是否相等,is 判断两个变量 的地址是不是相同,即两个变量是不是指向同一个。(有一些小小的例外)

 

0x04 深拷贝 和 浅拷贝 (重点)

浅拷贝:只拷贝地址

a = [11,22,33]

b = a #浅拷贝,只是将a的地址拷贝了过来

深拷贝:开辟新的内存,拷贝原变量的内容到新的内存中

import copy

a = [11,22,33]

c = copy.deepcopy(a)#深拷贝

深拷贝注意点:

深拷贝将c中元素指向的内存空间 的内容也 实现了拷贝。(递归拷贝)

如果只想深拷贝一层,不实现递归拷贝,可以使用copy.copy()

copy.copy(原变量)可以识别原变量是否是可变类型, 如果是可变类型只拷贝一层内容,如果不是可变类型,一层内容都不拷贝,纯粹浅拷贝。

0x05 私有化

_x :单前置下划线的变量,通过 from xxx import * 禁止导入。但是通过import 模块名的方式可以导入。 

__x:双前置下划线的变量,私有变量。只有在类内部可以访问。python是如何实现变量私有化的呢?其实python解释器进行了名字重整,将私有变量重新命名了,命名规则 _类名__私有变量。那么我们访问这个重命名的变量名 不就可以了吗!

0x06 property 的使用

class Money(object):
    def __init__(self):
        self.__money = 0
    def getMoney(self):
        return self.__money
    def setMoney(self,value):
        if isinstance(value,int):
            self.__money = value
        else:
            print("不是整数类型")
    money  = property(getMoney,setMoney)




mon = Money()

mon.money = 100 #自动调用setMoney
print(mon.money) #自动调用 getMoney

注意点:

mon.money 到底是调用getMoney还是setMoney,要根据实际的场景来判断,

1.如果是给mon.money赋值,那么一定是调用setMoney()

2.如果是获取mon.money的值,那么一定调用getMoney()

property的作用:相当于把方法进行了封装,开发者在对属性设置的时候更加方便。

实现方法二:

class Money(object):
    def __init__(self):
        self.__money = 0
    @property#该方法叫什么名,属性就是什么名,并且将该方法作为了get方法
    def money(self):
        return self.__money
   
    @money.setter 
    def money(self,value):
        if isinstance(value,int):
            self.__money = value
        else:
            print("不是整数类型")
    money  = property(getMoney,setMoney)




mon = Money()

mon.money = 100 #自动调用setMoney
print(mon.money) #自动调用 getMoney

0x04python是动态语言

如何在运行过程中给对象添加属性呢?

class Person(object):
    def __init__(self,newName,newAge):
        self.name = newName
        self.age = newAge

laowang = Person("laowang",99)
print(laowang.name)
print(laowang.age)

laowang.addr = "Beijing" #给对象添加实例属性
Person.num = 100 #给类添加类属性
print(laowang.num)
print(laozhang.num)

那么如何给对象添加方法呢?

class Person(object):
    def __init__(self,newName,newAge):
        self.name = newName
        self.age = newAge

laowang = Person("laowang",99)
print(laowang.name)
print(laowang.age)

laowang.addr = "Beijing" #给对象添加实例属性
Person.num = 100 #给类添加类属性
print(laowang.num)
print(laozhang.num)

def run(self,distance):
    print("ran %d m"%distance)

laowang.run = run #给对象添加实例方法
#laowang.run(10) #虽然laownag对象中 run属性已经指向了该函数,但是,这句代码还不正确,因为run属性指向的函数是后来添加的,即laowang.run()的时候,并没有把laowang自动作为第一个参数,导致该函数调用时出现缺少参数的问题。
#正确添加实例方法的方式
import types
laowang.run = types.MethodType(run,laowang)#给对象添加对象,表示将laowang作为第一个参数传给run
laowang.run(100)
#添加静态方法
@staticmethod
def test():
    print("---static method---")

Person.test = test
Person.test()

#添加类方法
@classmethod
def printNum(cls):
    print("---class method---")

Person.printNum = printNum
Person.printNum()

那么如何限制别人添加属性呢?可以使用__slots__

class Person(object):
    __slots__ = ("name","age") #限制此类只能有name和age两个属性


laowang = Person()
laowang.name = "laowang"
laowang.age = 11

laowang.score =100 #报异常,注意__slots__定义的属性仅对当前类的实例起作用,对继承的子类的实例对象不起作用。

0x04生成器

#列表生成式
a = [x*2 for x in range(1000000)]
#一次性生成所有元素在一个列表中,但是如果要生成的列表太大,那么python解释器将会杀死程序。那么如何生成有100000个元素的列表呢?用生成器!

#生成器:将列表生成式的方括号修改成圆括号 就变成了一个 生成器对象
b = (x*2 for x in range(1000000))

print(next(b)) #每执行一次生成一个元素,next()函数的返回值就是这个元素
print(next(b)) #每执行一次生成一个元素
print(next(b)) #每执行一次生成一个元素

斐波那契数列:生成器的第二种方式(重点)

如果一个函数中有yield关键字,那么这个函数就被称之为生成器。

def createNum():
        print("---start---")
        a,b = 0,1
        for I in range(5):
                print("---1---")
                yield b
                print("---2---")
                a,b = b,a+b

        print("--stop---")
#createNum()#错误,如果函数出现yield,这就不是一个函数了,而是一个生成器
#生成器的返回值是一个生成器对象。

a = createNum()

for i in range(6):
        print("第%d次"%i)
        b = next(a) #等价于b = a.__next()
        print(b)


"""
第0次
---start---
---1---
1
第1次
---2---
---1---
1
第2次
---2---
---1---
2
第3次
---2---
---1---
3
第4次
---2---
---1---
5
第5次
---2---
--stop---
Traceback (most recent call last):
  File "fei.py", line 17, in <module>
    b = next(a)
StopIteration

"""

当第一次调用next(a)时,程序会createNum从头开始执行,直到遇到yield b,就返回b,并且卡在这里。当下一次调用next(a)时,程序会从上一次卡住的地方继续执行。直到再一次遇到yield b卡住。但是可以看到程序出现了异常,为什么会报错呢?因为当主程序第六次循环时,createNum从上次卡住地方继续执行,这时它跳出了循环,无法返回一个yield b了。所以出现了异常。那么如果才能让程序不出现异常又打印出 stop呢?

def createNum():
        print("---start---")
        a,b = 0,1
        for I in range(5):
                print("---1---")
                yield b
                print("---2---")
                a,b = b,a+b

        print("--stop---")
#createNum()#错误,如果函数出现yield,这就不是一个函数了,而是返回生成器对象,不
能像调用函数一样调用

a = createNum()

for i in a:
        print(i)
"""
---start---
---1---
1
---2---
---1---
1
---2---
---1---
2
---2---
---1---
3
---2---
---1---
5
---2---
--stop---
"""

使用send

def test():
    I = 0
    while I < 5:
        temp = yield I #并不是将yield I 的返回值 赋值给了 temp,而是在下一次调用生成器时,可以给temp传一个值。
        print(temp)
        I+=1




t = test()
ret1 =t.__next__()
print(ret1)
ret2 = t.send("haha") #将值赋值给 temp,并从上一次函数停止的地方(上一次正好卡在了赋值的地方),开始继续执行直到遇到yield,然后,返回yield 之后的值。
print(ret2)
#注意send()的括号中必须有参数,如果你什么参数都不想传,那么你可以传一个None,表示空。给一个刚刚开始的生成器,用 t.send(None) 可以达到和 t.__next__() 相同的效果

多任务:看起来同时执行的任务

#协程
def test1():
    while True:
        print("1")
        yield None

def test2():
    while True:
        print("2")
        yield None

t1 = test1()
t2 = test2()

while True:
    t1.__next__()
    t2.__next__()

0x05 迭代器

什么是迭代?迭代即在上一次的xxx的基础上做下一次的xxx。例如版本的快速迭代,指的是在上一个版本的基础上开发下一个版本。在程序中,for i in range(10) 就是在上一次赋值的基础上,赋下一个值。

1.可迭代对象:

  1. 集合数据类型:如列表、元组、字典、集合、字符串等
  2. 生成器
#用 for循环 迭代 生成器

b = (x for x in range(10))

for temp in b:
    print(temp)

2.判断一个对象是不是可以迭代:

from collection import Iterable

isinstance("abc",Iterable) #判断 字符串”abc“ 是不是 可迭代类 的一种实例 

3.迭代器

可以被next() 函数调用 并不断返回下一个值的对象 称为 迭代器:iterator

生成器一定是迭代器

如何判断一个对象是不是一个迭代器对象呢?

from collections import Iterator

isinstance((x for x in range(10)),Iterator) #判断一个对象是不是迭代器对象

列表不是迭代器对象,但是可以强制类型转化为迭代器对象,转化之后占用的内存空间一定比原来要少

a = [11,22,33]

b = iter(a) #将a强制转换为迭代器 对象

next(b)

0x06 闭包

在python中,函数名 就是 函数指针,指向存储函数体代码的内存段。

def test():
    print("---1----")


test() #调用该函数
b = test
b() #通过b加上括号同样可以调用该函数

那么什么是闭包呢?

python 中闭包的概念和js中闭包的概念几乎是完全相同的,友情链接:https://mp.csdn.net/postedit/91310802

闭包 = 函数地址 + 与之相关的环境

首先,请看如下代码:

def test():
    num = "hello world"
    def test2():
        print(num)
    test2()

test2()函数中并没有声明num 这个变量(像这种本地作用域中没有的变量在js中称为自由变量),调用test2()时怎么能直接输出num的值呢?很简单,test2的上层作用域中声明了这个变量num,并且变量num的作用域是整个test()函数,这其中就包括test2()函数。

那么如果我们这样写呢?

def test():
    num = "hello world"
    def test2():
        print(num)
    return test2

ret = test()
ret() #hello world

有人就要发问了,我们返回了test2函数的地址,然后通过地址来调用test2函数,这个时候,num是凭空出现的?怎么可能还能输出hello world呢?其实,不论是python还是js,在返回一个函数的引用的时候,还同时返回了 与之相关的一个环境, 这个环境中有 与这个函数相关的所有 变量的声明和定义,这个环境 闭合了函数中 所有的自由变量,让函数 变得 完整。函数的引用加上 环境 就称之为 一个闭包,也就说,看似只返回了 函数的引用,其实返回了 一个 闭包.

last but not least! python 中没有办法像 js中一样用闭包创建一个 计数器。报错 局部变量错误,具体原因我还在思考。。。嘿嘿嘿,有大佬知道麻烦留个言哈

0x07 装饰器

装饰器能干啥?装饰能在不改变原来函数定义的情况下,对函数进行重写。

装饰器是程序开发中经常会用到的一个功能,用好了装饰器,开发效率如虎添翼,所以这也是python面试中必问的问题,但对于好多初次接触这个知识的人来讲,这个功能有点绕,自学时直接饶了过去,然后面试问到了就挂了,因为装饰器是程序开发的基础知识,这个都不会,别跟人家说你会python,看了下面的文章,保证你学会装饰器。

首先看懂以下代码:

def test1():
    print("---1---")

def test1():
    print("---2---")

test1() #---2--- 原因:python解释器首先开辟一段内存存储该函数的第一次定义,并将首地址赋值给test1这个变量,然后python解释器又开辟了一段内存来存储 test1()函数的第二次定义,并将首地址赋值给了test1这个变量,这次新的赋值 覆盖了 原来的赋值,所以现在 test1中存储的是 新的函数定义 的地址,所以自然而然 输入---2----

企业中写代码一般遵循“开放封闭”原则,所谓封闭,即已经实现的功能代码块你最好不要动,所谓开放,即你可以在已经实现的代码块不变更的情况下,对其扩展。

例如:有如下代码

def f1():
    print("f1")

def f2():
    print("f2")

def f3():
    print("f3")

def f4():
    print("f4")



f1()
f2()
f3()
f4()

 现在需要在每个函数调用前加上一个验证身份的功能,让能验证通过的人才能调用这些函数,那么根据开放封闭原则,就不能如下这样改:

def check():
    xxxxx
    xxxxx


def f1():
    check()
    print("f1")

def f2():
    check()
    print("f2")

def f3():
    check()
    print("f3")

def f4():
    check()
    print("f4")



f1()
f2()
f3()
f4()

因为这样改,动了已经实现的代码块。那么应该怎么修改呢?这就要用到修饰器了

def w1(func):
    def inner():
        xxxxx
        xxxxx
        #验证功能
        func()
    return inner



@w1
def f1():
    print("f1")
@w1
def f2():
    print("f2")
@w1
def f3():
    print("f3")
@w1
def f4():
    print("f4")



f1()
f2()
f3()
f4()

那么装饰器的原理是什么呢?先来理解如下代码:

def w1(func):
    def inner():
        print("---正在验证权限---")
        func()
    return inner


def f1():
    print("---f1---")


def f2():
    print("---f2---")


innerFunc = w1(f1)

innerFunc()

那么假如将以上代码修改成这样呢?

def w1(func):
    def inner():
        print("---正在验证权限---")
        func()
    return inner


def f1():
    print("---f1---")


def f2():
    print("---f2---")


f1 = w1(f1)  #对f1重新赋值
 
f1()

那么在函数上面写上@w1 就相当于 f1 = w1(f1)

那么假如一个函数加了两层装饰器该如何理解呢?

def makeBold(fn):
    def wrapped():
        print("111")
        return "<b>"+fn()+"</bn>"
    return wrapped


def makeItalic(fn):
    def wrapped():
        print("---2---")
        return "<I>"+fn()+"</I>"
    return wrapped

@makeBold #这个装饰器装饰的是 下面 这个装饰器 和函数组成的整体
@makeItalic #等价于 在函数后面加上 test3 = makeItalic(test3)
def test3():
    print("---3----")
    return "hello world"

print(test3())

"""
111
---2---
---3----
<b><I>hello world</I></bn>
"""

先从最近的装饰器开始装饰,然后逐个装饰较远的。

@makeBold 
@makeItalic
def test3():
    print("---3----")
    return "hello world"

这段代码等价于:


def test3():
    print("---3----")
    return "hello world"

test3 = makeItalic(test3)
test3 = makeBold(test3)

那么如何对有参数的函数进行装饰呢?那还不简单,给内层函数加上两个参数不就可以了

def zhuangshi(func):
    def inner(a,b):
        print("zhuangshi")
        func(a,b)
    return inner

@zhuangshi
def test(a,b):
    print(a)
    print(b)

#test = zhuangshi(test)
test(1,2)


如果被装饰的函数有返回值,那么在重写内层函数的时候记得 将 原来函数的返回值 返回

def zhuangshi(func):
    def inner(a,b):
        print("zhuangshi")
        ret = func(a,b)
        return ret
    return inner

@zhuangshi
def test(a,b):
    print(a)
    print(b)
    return "haha"

#test = zhuangshi(test)
rtn = test(1,2)
print(rtn)

通用装饰器:在内层函数中加上不定长参数和return语句,那么这个装饰器可以适用于任何函数

def func(fuctionName):
    def func_in(*args,**kwargs):
        ret = functionName(*args,**kwargs)
        return ret
    return func_in

@func
def test():
    print("hhh")
    return "haha"

@func
def test2():
    print("---test2---")


@func
def test3(a):
    print(a)
    

带有参数的装饰器:带有参数的装饰器,能够实现 在运行时完成不同的功能

def zaitaoyiceng(canshu):
    def func(fuctionName):
        def func_in(*args,**kwargs):
            if canshu == 123:
                print("欢迎老板")
                ret = functionName(*args,**kwargs)
            else:
                print("小臂崽子上线了?")
                ret = functionName(*args,**kwargs)
            return ret
        return func_in
    return func
"""
1.先执行zaitaoyiceng(123)函数,这个函数return的结果是func这个函数的引用
2.@func
3.使用@func对test()进行装饰
"""

@zaitaoyichang(123) #相当于先执行该函数,然后返回一个闭包 然后执行@func
def test():
    print("hhh")
    return "haha"


@zaitaoyichang(111)
def test2():
    print("hh")
    return "h"

test()
test2()

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值