Python 核心编程 (全)

浅拷贝和深拷贝python

1.浅拷贝:是对于一个对象的顶层拷贝,通俗的理解是:拷贝了引用,并无拷贝内容。至关于把变量里面指向的一个地址给了另外一个变量就是浅拷贝,而没有建立一个新的对象,如a=bc++

2.深拷贝:首先要import copy,而后c = copy.deepcopy(a),就表示把a的内容深拷贝到c中,若是发现了a中也存在引用的内容,则递归拷贝,也就是把当前的这个引用的对象继续深拷贝编程

3. copy和deepcopy的区别
    ①copy:浅拷贝,里面若是有可变类型,修改这个可变类型(如list),被拷贝的对象也会相应改变,仅仅拷第一层,若是是不可变类型,就一层都不拷,若是是可变类型就拷一层
    ②deepcopy:深拷贝,里面无论是可变类型和不可变类型,被拷贝的对象都不会受到影响,递归拷贝缓存

4.copy和deepcopy拷贝元组的特色
    使用copy模块的copy功能的时候,它会根据当前拷贝的数据类型是可变类型仍是不可变类型有不一样的处理方式,如元组是不可变类型,拷贝多份没有用,对copy来讲,若是是可变类型就拷一层,若是是不可变类型,就一层都不拷闭包

属性propertyapp

1.属性property-1
    ①私有属性添加getter和setter方法
    ②使用property升级getter和setter方法编程语言

num = property(getNum,setNum)

    注意:
        Num究竟是调用getNum()仍是setNum(),要根据实际的场景来判断,值得注意的是必定要先填getNum后setNum
        若是是给t.num赋值,那么必定调用setNum()
        若是是获取t.num的值,那么就必定调用getNum()
        property的做用:至关于把方法进行了封装,开发者在对属性设置数据的时候更方便 函数式编程

2.属性property-2函数

class Money(object):
    @property #修饰器
    def num(self): 
        print("------getter-----")
        return self.__num

    @num.setter #修饰器
    def num(self,new_num):
        print("------setter------")
        self.__num = new_num
        t.num = 20
        print(t.num)

迭代器工具

1.迭代器
    迭代是访问集合元素的一种方式。迭代器是一个能够记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到全部的元素被访问完结束,迭代器只能往前不会后退

2.可迭代对象(for 循环遍历的数据类型)
    ①一类是集合数据类型,如 list 、 tuple 、 dict 、 set 、 str 等
    ②一类是 generator(列表生成式,生成器) ,包括生成器和带 yield 的generator function
    ③这些能够直接做用于for循环的对象统称为可迭代对象:Iterable 

from collections import Iterable
# 若是能够迭代就返回True
isinstance([ ], Iterable)

3.判断是否能够迭代
    可使用isinstance()判断一个对象是不是Iterable对象

from collections import Iterable
# 若是能够迭代就返回True
isinstance([ ], Iterable)

    而生成器不但能够做用于for循环,还能够被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示没法继续返回下一个值了

4.迭代器
    ①能够被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator
    ②可使用isinstance()判断一个对象是不是Iterator对象
    ③生成器(i for i in range(10))必定是迭代器,但迭代器不必定是生成器
    ④迭代器必定是可迭代对象,但可迭代对象不必定是迭代器      

from collections import Iterator
isinstance((x for x in range(10)), Iterator)  # 若是是的话就返回True

5.iter( )函数
    ①生成器都是Iterator(迭代器)对象,但 list、dict、str虽然是Iterable(可迭代),却不是Iterator(迭代器)
    ②把list、dict、str 等 Iterable(可迭代)变成 Iterator(迭代器)可使用iter()函数,就比如人能够游泳,但不是天生就会,可迭代对象就比如人,迭代器就比如会游泳的人,须要通过iter()训练同样

isinstance(iter([ ]), Iterator)
True

闭包

1.函数的引用     

test1() # 调用函数
ret = test1 # 引用函数
ret() # 经过引用调用函数

2.什么是闭包
    在函数内部再定义一个函数,而且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包      

def test(number):
    print("-----1-----")
    def test_in(number2):
        print("----2-----")
        print(number+number2)
    
    print("------3------")
    # 把函数的引用返回了
    return test_in

# 用来接收test(100),指向了一个函数体,这个100传给了number
ret = test(100)
# 这个1传给了number2
ret(1) # 这个返回101
ret(100) # 这个返回200
ret(200) # 这个返回300

3.闭包再理解
    内部函数对外部函数做用域里变量的引用(非全局变量),则称内部函数为闭包
    闭包的实际例子:

def line_conf(a, b):

    def line(x):
        return a*x + b
    return line

line1 = line_conf(1, 1)
line2 = line_conf(4, 5)

print(line1(5))
print(line2(5))

    这个例子中,函数line与变量a,b构成闭包。在建立闭包的时候,咱们经过line_conf的参数a,b说明了这两个变量的取值,这样,咱们就肯定了函数的最终形式(y = x + 1和y = 4x + 5)。咱们只须要变换参数a,b,就能够得到不一样的直线表达函数。由此,咱们能够看到,闭包也具备提升代码可复用性的做用

装饰器

1.装饰器
    在有两个重名的函数中,Python解释器会调用最后定义的那重名函数,由于在Python里,第一个函数指向的是一片内存,而后又让这个函数指向另外一片内存,就会利用第二片内存来执行,全部函数名应尽可能避免相同
    写代码要遵循开放封闭原则,虽然在这个原则是用的面向对象开发,可是也适用于函数式编程,简单来讲,它规定已经实现的功能代码不容许被修改,但能够被扩展,即:
    ①封闭:已实现的功能代码块
    ②开放:对扩展开发
    ③实例:

def w1(func):
    def inner():
        # 验证1
        # 验证2
        # 验证3
        func()
    return inner

@w1 # 装饰器
def f1():
    print('f1')

@w1  # 装饰器
def f2():
    print('f2')

........

    对于上述代码,也是仅仅对基础平台的代码进行修改,就能够实如今其余人调用函数 f一、 f2 、f3 、f4 以前都进行【验证】操做,而且其余业务部门无需作任何操做

2.装饰器的功能
    ①引入日志
    ②函数执行时间统计
    ③执行函数前预备处理    
    ④执行函数后清理功能
    ⑤权限校验等场景
    ⑤缓存
    ⑥若是是有多个装饰器的状况,通常是先装饰最下面的一个,而后依次往上,@w1类比于f1 = w1(f1)

3.装饰有参数的函数:在传递参数的时候,须要在闭包里面定义一个形参,闭包里面的调用的函数也要定义一个形参,不然会致使两部分函数调用失败

4.装饰不定长的参数的函数:在传递参数的时候,须要在闭包里面定义一个*args和**kwargs,闭包里面的调用的函数也要定义一个*args和**kwargs,这样就能够在调用的时候传递任意长度的参数,增长代码的可复用性

5.装饰带返回值的函数:须要在闭包里面进行一个接收,也就是ret = test(),而后再把接收到的ret return出去,这样在装饰的test才能返回出当前须要返回的东西,不然只会返回None

6.通用的装饰

def w1(func):
    print("-----正在装饰-----")
    def inner(*args,**kwargs):
        print("---正在验证权限---")
        print("----记录日志----")
        ret = func(*args,**kwargs) 
        return ret 
    return inner

    带有参数的装饰器:也就是在原来包含一个闭包的函数外面再给他套一个函数,用来传递装饰器的参数

def func_arg(arg):
    def w1(func):
        print("---记录日志---")
        def inner(*args,**kwargs):
            func(*args,**kwargs) 
        return inner
    return w1

@func_arg("heihei") 
def f1():
    print("----f1----")               

# 1.先执行func_arg("heihei")函数,这个函数return的结果是
# 2.@w1
# 3.使用@w1对f1进行装饰
# 做用:带有参数的装饰器,可以起到在运行时,有不一样的功能

python是动态语言

1.Python是动态语言
    动态编程语言是高级程序设计语言的一个类别,在计算机科学领域已被普遍应用。它是一类在运行时能够改变其结构的语言:例如新的函数、对象、甚至代码能够被引进,已有的函数能够被删除或是其余结构上的变化。这种动态语言的应用就比如是在没有更新app的状况下,它的界面在后台也能够被开发者更改,由于它是动态的,能够把新增的动态程序放置在文本,只要加载一遍便可

2.运行的过程当中给对象绑定(添加)属性:也就是说给对象绑定一个实例属性(这个属性是初始化以外的额外属性),只有这个建立对象的属性如laozhao.addr = "北京"

3.运行的过程当中给类绑定(添加)属性:若是须要全部的一个类的实例加上一个属性怎么办呢? 答案就是直接给这个类绑定属性,如Person.sex = "male"

4.运行的过程当中给类绑定(添加)方法: 若是是对这个类绑定一个实例方法,那么就要先import types,而后如对象.方法名 = types.MethodType(函数名, 对象),把run这个方法绑定到P对象上。若是是静态方法和类方法,就直接用类名.方法名=函数名

5.运行的过程当中删除属性、方法:
    ①del 对象.属性名 
    ②delattr(对象, "属性名")

     __slots__的做用

1.动态语言:能够在运行的过程当中,修改代码

2.静态语言:编译时已经肯定好代码,运行过程当中不能修改

3.为了达到限制的目的,Python容许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性,如__slots__ = ("name","age"),就能够达到限制name和age的属性,若是发现有添加其余属性的程序就会发生异常

4.使用__slots__要注意,__slots__定义的属性仅对当前类实例起做用,对继承的子类是不起做用的

生成器

1.什么是生成器
    经过列表生成式,咱们能够直接建立一个列表。可是,受到内存限制,列表容量确定是有限的。并且,建立一个包含100万个元素的列表,不只占用很大的存储空间,若是咱们仅仅须要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了 
    ①while的列表推导            

list.append(i)

    ②for的列表推导,range与切片很相似

for i in range(10,78):

    ③第一个i是元素的值,后面的for是循环的次数,若是第一个i=11,那么全部的元素都是11  

a=[i for i in range(1,18)]

    ④for控制循环的次数,for和if的嵌套       

c = [i for i in range(10) if i%2==0]

    ⑤每执行第一个for循环都要执行第二个for循环的全部次数   

d = [i for i in range(3) for j in range(2)]

    ⑥每执行第一个for循环都要执行第二个for循环的全部次数

d = [(i,j) for i in range(3) for j in range(2)]

     例题:找出100之内能被3整除的正整数

aiquot = []               
for n in range(1,100)
    if n%3 ==0:
        aiquot.append(n)

range(3,100,3) # 很简洁

2.建立生成器方法1
    要建立一个生成器,有不少种方法。第一种方法很简单,只要把一个列表生成式的[ ]改为( )
    如L = [ x*2 for x in range(5)]和G = ( x*2 for x in range(5)),L是一个列表,而G是一个生成器,能够经过next(G)函数得到生成器的下一个返回值,不断调用 next()实在是太变态了,正确的方法是使用for循环,由于生成器也是可迭代对象

3.建立生成器方法2
    fib函数变成generator,只须要把print(b)改成yield b就能够了,循环过程当中不断调用yield ,就会不断中断。固然要给循环设置一个条件来退出循环,否则就会产生一个无限数列出来,当循环到没有元素的时候,将会生成异常,这时候就要用try和exception来检测异常,#print自动检测异常并中止,可是next()就要用try ,在建立生成器的时候须要接收函数的返回值
    #1.next(返回函数名)

    #2.返回函数名.__next__()是同样的方法来获取下一个返回值
    总结:生成器是这样一个函数,它记住上一次返回时在函数体中的位置。对生成器函数的第二次或第 n 次调用跳转至该函数中间,而上次调用的全部局部变量都保持不变,生成器不只“记住”了它数据状态;生成器还“记住”了它在流控制构造(在命令式编程中,这种构造不仅是数据值)中的位置

4.生成器的特色
    ①节约内存
    ②迭代到下一次的调用时,所使用的参数都是第一次所保留下的,便是说,在整个全部函数调用的参数都是第一次所调用时保留的,而不是新建立的

5.send用法
    ①若是在在程序中有个变量等于yield,不是说把yield的值给了这个变量,而是接下来在下一次调用执行一次的时候能够传一个值,t.send("haha")和t.__next__()均可以让生成器继续执行,不一样的是send能够传递一个值,可是不能在程序刚开始执行就用send传值,有两种方法,要么先用__next__调用一次,再send一个值,或者t.send(None)
    ②生成器:完成多任务,控制多个任务执行的状况

元类

1.元类
    ①类也是对象
    ②在大多数编程语言中,类就是一组用来描述如何生成一个对象的代码段,类一样也是一种对象

2.动态的建立类
    由于类也是对象,你能够在运行时动态的建立它们,就像其余任何对象同样

def choose_class(name):
    if name == 'foo':
        class Foo(object):
            pass
        return Foo     # 返回的是类,不是类的实例
    else:
        class Bar(object):
            pass
        return Bar

MyClass = choose_class('foo') # 当你使用class关键字时,Python解释器自动建立这个对象

2.使用type建立类
    ①type还有一种彻底不一样的功能,动态的建立类,type能够像这样工做:
    ②type(类名, 由父类名称组成的元组(针对继承的状况,能够为空),包含属性的字典(名称和值)) 

Test2 = type("Test2",(),{}) #定了一个Test2类

3.使用type建立带有属性的类

Foo = type('Foo', (), {'bar':True})

4.使用type建立带有方法的类

FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar}) # 这是添加实例方法echo_bar    
Foochild = type('Foochild', (Foo,), {"echo_bar":echo_bar, "testStatic": testStatic}) # 添加静态方法
Foochild = type('Foochild',(Foo,),{"echo_bar":echo_bar, "testStatic":testStatic, "testClass":testClass}) # 添加类方法

5.到底什么是元类
    元类就是用来建立类的东西,元类就是用来建立这些类 (对象) 的,元类就是类的类,元类又由元类建立,Python中全部的东西,注意,我是指全部的东西——都是对象。这包括整数、字符串、函数以及类

6.__metaclass__属性

class Foo(object):
   __metaclass__ = something…

    若是这么作了,Python就会用元类来建立类Foo,这里面有些技巧。首先写下class Foo(object),可是类Foo尚未在内存中建立。Python会在类的定义中寻找__metaclass__属性,若是找到了,Python就会用它来建立类Foo,若是没有找到,就会用内建的type来建立这个类

GC垃圾回收

1.GC垃圾回收
    ①小整数对象池:Python为了优化速度,使用了小整数对象池, 避免为整数频繁申请和销毁内存空间。Python 对小整数的定义是[-5, 257) 这些整数对象是提早创建好的,不会被垃圾回收
    ②大整数对象池:每个大整数,均建立一个新的对象
    ③intern机制:假如要建立n个对象的是同样的字符串,那么python只会建立一个内存空间来存储,其余对象都是引用,但若是字符串中出现空格或其余符号就表示为不一样的对象

2.GC(Garbage collection)垃圾回收
    Python里也同Java同样采用了垃圾收集机制,不过不同的是: Python采用的是引用计数机制为主,标记-清除和分代收集两种机制为辅的策略

3.引用计数机制的优势
    ①简单
    ②实时性:一旦没有引用,内存就直接释放了。不用像其余机制等到特定时机。实时性还带来一个好处:处理回收内存的时间分摊到了平时

4. 引用计数机制的缺点
    ①维护引用计数消耗资源
    ②循环引用

5.GC系统所承担的工做远比"垃圾回收"多得多。实际上,它们负责三个重要任务:
    ①为新生成的对象分配内存
    ②识别那些垃圾对象
    ③从垃圾对象那回收内存

6.垃圾回收机制:Python中的垃圾回收是以引用计数为主,分代收集为辅
    ①致使引用计数+1的状况:
        对象被建立,例如a=23
        对象被引用,例如b=a
        对象被做为参数,传入到一个函数中,例如func(a)
        对象做为一个元素,存储在容器中,例如list1=[a,a]
    ②致使引用计数-1的状况:
        对象的别名被显式销毁,例如del a
        对象的别名被赋予新的对象,例如a=24
        一个对象离开它的做用域,例如f函数执行完毕时,func函数中的局部变量(全局变量不会)
        对象所在的容器被销毁,或从容器中删除对象

7.查看一个对象的引用计数

import sys
a = "hello world"
sys.getrefcount(a)

    ①能够查看a对象的引用计数,可是比正常计数大1,由于调用函数的时候传入a,这会让a的引用计数+1
    ②有三种状况会触发垃圾回收:
        调用gc.collect()
        当gc模块的计数器达到阀值的时候
        程序退出的时候

8.gc模块的自动垃圾回收机制:

    ①必需要import gc模块,而且is_enable()=True才会启动自动垃圾回收。
    ②这个机制的主要做用就是发现并处理不可达的垃圾对象。
    ③垃圾回收 = 垃圾检查 + 垃圾回收
    ④在Python中,采用分代收集的方法。把对象分为三代,一开始,对象在建立的时候,放在一代中,若是在一次一代的垃圾检查中,该对象存活下来,就会被放到二代中,同理在一次二代的垃圾检查中,该对象存活下来,就会被放到三代中
    ⑤gc模块里面会有一个长度为3的列表计数器,能够经过gc.get_count()获取,gc.set_threshold(threshold0[, threshold1[, threshold2]) 设置自动执行垃圾回收的频率,例如(700,10,10)每一次计数器的增长,gc模块就会检查增长后的计数是否达到阀值的数目,700表示阈值,10表示没清理10次零代就清理一次二代,第二个10表示每清理10次一代链表就清理二代一次
    注意点:
        gc模块惟一处理不了的是循环引用的类都有__del__方法,因此项目中要避免定义__del__方法

内建属性

    经常使用专有属性             说明                                    触发方式
    __init__                  构造初始化函数                  建立实例后,赋值时使用,在__new__后
    __new__                生成实例所需属性               建立实例时
    __class__              实例所在的类                       实例.__class__
    __str__                  实例字符串表示,可读性     print(类实例),如没实现,使用repr结果
    __repr__               实例字符串表示,准确性      类实例 回车 或者 print(repr(类实例))
    __del__                 析构                                     del删除实例
    __dict__                实例自定义属性                    vars(实例.__dict__)
    __doc__                类文档,子类不继承            help(类或实例)
    __getattribute__    属性访问拦截器                    访问实例属性时
    __bases__             类的全部父类构成元素         类名.__bases__

def __getattribute__(self,obj):
    if obj == 'subject1':
        print('log subject1')
        return 'redirect python'
    else:  # 测试时注释掉这2行,将找不到subject2
        return object.__getattribute__(self,obj)

    __getattribute__的做用能够用来打印Log日志
    __getattribute__的坑:

class Person(object):
    def __getattribute__(self,obj):
        print("---test---")
        if obj.startswith("a"):
            return "hahha"
        else:
            return self.test    
    def test(self):
        print("heihei")

t.Person()
t.a #返回hahha
t.b #会让程序死掉
# 缘由是:当t.b执行时,会调用Person类中定义的__getattribute__方法,可是在这个方法的执行过程当中if条件不知足,因此 程序执行else里面的代码,即return self.test  问题就在这,由于return 须要把self.test的值返回,那么首先要获self.test的值,由于self此时就是t这个对象,因此self.test就是t.test 此时要获取t这个对象的test属性,那么就会跳转到__getattribute__方法去执行,即此时产生了递归调用,因为这个递归过程当中 没有判断何时推出,因此这个程序会永无休止的运行下去,又由于每次调用函数,就须要保存一些数据,那么随着调用的次数愈来愈多,最终内存吃光,因此程序崩溃
# 注意:之后不要在__getattribute__方法中调用self.xxxx

调试

1.调试:pdb是基于命令行的调试工具,很是相似gnu的gdb(调试c/c++)
    执行时调试
    程序启动,中止在第一行等待单步调试  

python -m pdb xxx.py

2.调试方法     ①n(next)执行下一步       ②l(list)显示当前执行进度      ③c(continue)继续执行代码      ④b(break)添加断点      ⑤q(quit)停止并退出      ⑥clear num删除指定断点      ⑦p(print)打印变量的值      ⑧a(args)打印全部的形参数据      ⑨s(step)进入到一个函数      r执行代码直到从当前函数返回

相关文章
相关标签/搜索