1. 元类

1.1 类也是对象

>>> class Student(object):
...     pass
...

使用关键字class,Python解释器在执行的时候就会创建一个对象
以上代码段将在内存中创建一个对象,名字就是Student 这个对象(类对象Student)拥有创建对象(实例对象)的能力。但是,它的本质仍然是一个对象,于是乎你可以对它做如下的操作:
1.你可以将它赋值给一个变量
2.你可以拷贝它
3.你可以为它增加属性
4.你可以将它作为函数参数进行传递

>>> s = Student()
>>> print(s)
<__main__.Student object at 0x000001E168A65278>
>>> print(Student) # 你可以打印一个类,因为类也是一个对象
<class '__main__.Student'>
>>> def echo(o):
...     print(o)
...
>>> echo(Student) # 可以将类作为参数传递给函数
<class '__main__.Student'>
>>> print(hasattr(Student,'name'))
 False
>>> Student.name = 'hanmeimei' # 为类添加属性
>>> print(hasattr(Student,'name'))
 True
>>> print(Student.name)
hanmeimei
>>> Stu = Student  # 将类赋值给一个变量
>>> print(Stu())
<__main__.Student object at 0x000001E168A653C8>

1.2 动态地创建类

 内建函数 type

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。
>>> class Student(object):
...     pass
...
>>> s = Student()
>>> print(type(1)) 
<class 'int'>     # 数值的类型
>>> print(type('abc'))
<class 'str'>     # 字符串的类型
>>> print(type(s))
<class '__main__.Student'> # 实例对象类型
>>> print(type(Student))   # 类的类型
<class 'type'>
>>> type(str)
<class 'type'>
>>> type(int)
<class 'type'>
>>> type(dict)
<class 'type'

使用type 动态创建类

函数type实际上是一个元类。type就是Python在背后用来创建所有类的元类。Python中所有的东西,包括整数、字符串、函数以及类,它们全部都是对象,而且它们都是从一个类创建而来,这个类就是type

>>> age = 30
>>> age.__class__
<class 'int'>
>>> name = 'hanmeimei'
>>> name.__class__
<class 'str'>
>>> def foo():
...     pass
...
>>> foo.__class__
<class 'function'>
>>> class Bar(object):
...     pass
...
>>> b = Bar()
>>> b.__class__
<class '__main__.Bar'>
>>> age.__class__.__class__
<class 'type'>
>>> name.__class__.__class__
<class 'type'>
>>> foo.__class__.__class__
<class 'type'>
>>> b.__class__.__class__
<class 'type'>

type 创建类

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

>>> Foo = type('Foo',(),{'bar':True})
>>> def echo_bar(self):     # 普通方法
...     print(self.bar)
...
>>> @staticmethod       # 静态方法
... def testStatic():
...     print("static method ......")
...
>>> @classmethod        # 类方法
... def testClass(cls):
...     print(cls.bar)
...
>>> Foochild = type('Foochild',(Foo,),{"echo_bar":echo_bar,"testStatic":testStatic,"testClass":testClass})
>>> print(Foochild)
<class '__main__.Foochild'>
>>> child = Foochild()  # 实例化
>>> print(child)
<__main__.Foochild object at 0x0000020A62AC54E0>
>>> child.bar   # 从父类继承的 bar 属性
True
>>> child.echo_bar()    # 调用普通方法
True
>>> child.testStatic()  # 调用静态方法
static method ......
>>> child.testClass()   # 调用类方法
True

 2. python是动态语言

2.1 动态添加删除属性

>>> class Person(object):
...     def __init__(self,name=None,age=None):
...             self.name=name
...             self.age=age
...
>>> p = Person('hanmeimei',20)
>>> p.sex = 'female'    # 为对象动态添加属性
>>> p.sex
'female'
>>> p1 = Person('lilei',19)     
>>> p1.sex      # 上面是为 p 对象添加属性,p1 并没有'sex'属性
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Person' object has no attribute 'sex'
>>> Person.sex=None     # 为类动态添加属性
>>> p1 = Person('lilei',19)
>>> print(p1.sex)# 为类动态添加属性后实例化出来的对象也就有sex属性
None
>>> p.sex
'female'
>>> del p.sex   # 动态删除对象属性
>>> print(p.sex)    # 对象p仍有类属型sex
None
>>> del Person.sex  # 动态删除类属性
>>> p.sex
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Person' object has no attribute 'sex'

2.2 动态添加方法

>>> class Person(object):
...     def __init__(self,name,age):
...             self.name=name
...             self.age=age
...
>>> def run(self):
...     print('------%s is running------' %self.name)
...
>>> p = Person('lilei',20)
>>> import types
>>> p.run = types.MethodType(run,p)     # 为对象动态添加方法
>>> p.run()
------lilei is running------


import types

class Person(object):
    num = 0
    def __init__(self, name = None, age = None):
        self.name = name
        self.age = age
    def eat(self):
        print("eat food")

#定义一个实例方法
def run(self, speed):
    print("%s在移动, 速度是 %d km/h"%(self.name, speed))

#定义一个类方法
@classmethod
def testClass(cls):
    cls.num = 100

#定义一个静态方法
@staticmethod
def testStatic():
    print("---static method----")

#创建一个实例对象
P = Person("老王", 24)
#调用在class中的方法
P.eat()

#给这个对象添加实例方法
P.run = types.MethodType(run, P)
#调用实例方法
P.run(180)

#给Person类绑定类方法
Person.testClass = testClass
#调用类方法
print(Person.num)
Person.testClass()
print(Person.num)

#给Person类绑定静态方法
Person.testStatic = testStatic
#调用静态方法
Person.testStatic()

>>>
eat food
老王在移动, 速度是 180 km/h
0
100
---static method----

2.3 __slots__属性

动态语言:可以在运行的过程中,修改代码

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

如果我们想要限制实例的属性怎么办?比如,只允许对Person实例添加name和age属性。

为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制class实例能添加的属性:

>>> class Person(object):
...     __slots__ = ('name','age')
...
>>> p = Person()
>>> p.name = 'hanmeimei'
>>> p.age = 19
>>> p.score = 90
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Person' object has no attribute 'score'

__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的

除非在子类中也定义__slots__,这样,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__

3. 生成器

Python中,这种一边循环一边计算的机制,称为生成器:generator

创建生成器的方式一:把列表生成式的[] 改为()

>>> L = [ x*2 for x in range(5)]
>>> L
[0, 2, 4, 6, 8]
>>> G = ( x*2 for x in range(5) )
>>> G
<generator object <genexpr> at 0x000001AF9F383AF0>
>>> next(G)    # 等价于 G.__next__()
0
>>> next(G)
2
>>> next(G)
4
>>> next(G)
6
>>> next(G)
8
>>> next(G)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> G = ( x*2 for x in range(5) )
>>> for x in G:
...     print(x)
...
0
2
4
6
8

生成器保存的是算法,每次调用 next(G) ,就计算出 G 的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出 StopIteration 的异常,

我们创建了一个生成器后,基本上永远不会调用 next() ,而是通过 for 循环来迭代它,并且不需要关心 StopIteration 异常。

创建生成器的方式二:利用 yield 

>>> def fib():
...     print('------start------')
...     a,b = 0,1
...     for i in range(5):
...             print('------step1------')
...             yield b
...             print('------step2------')
...             a,b = b,a+b
...             print('------step3------')
...     print('------end------')
...
>>> fib()
<generator object fib at 0x000001AF9F383AF0>
>>> a = fib()
>>> next(a)
------start------
------step1------
1
>>> next(a)
------step2------
------step3------
------step1------
1
>>> next(a)
------step2------
------step3------
------step1------
2
>>> next(a)
------step2------
------step3------
------step1------
3
>>> next(a)
------step2------
------step3------
------step1------
5
>>> next(a)
------step2------
------step3------
------end------
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

def fib():
     print('------start------')
     a,b = 0,1
     for i in range(5):
         yield b
         a,b = b,a+b
     print('------end------')
for num in fib():   # 利用循环遍历生成器
    print(num)
    
'''
------start------
1
1
2
3
5
------end------
'''

send()方法

>>> def test():
...     i = 0
...     while i < 5:
...             temp = yield i       # yield i 表达式的值并不是 i 的值
...             print(temp)
...             i += 1
...
>>> t = test()
>>> t.__next__()
0
>>> t.__next__()
None
1
>>> t.__next__()
None
2
>>> t.send("haha...")    # send()方法会执行__next()__ 并将参数作为表达式 yield i 的结果传给 temp 
haha...
3

>>> def test():
...     i = 0
...     while i < 5:
...             temp = yield i
...             print(temp)
...             i += 1
...
>>> t = test()
>>> t.send("haha...") # send()方法不能在生成器生成第一个时传入非空值
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't send non-None value to a just-started generator
>>> t.send(None)
0
>>> t.send("haha...")
haha...
1
>>> t.__next__()
None
2

完成多任务(协程)

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__()

------1------
------2------
------1------
------2------
------1------
------2------
------1------
------2------
------1------
------2------
......

 4. 迭代器

4.1 可迭代对象

可以直接作用于 for 循环的数据类型有以下几种:

一类是集合数据类型,如 list 、 tuple 、 dict 、 set 、 str 等;

一类是 generator ,包括生成器和带 yield 的generator function。

这些可以直接作用于 for 循环的对象统称为可迭代对象: Iterable 。

可以使用 isinstance() 判断一个对象是否是 Iterable 对象:

>>> from collections import Iterable
>>> isinstance([],Iterable)
True
>>> isinstance({},Iterable)
True
>>> isinstance('hello',Iterable)
True
>>> isinstance((x for x in range(5)),Iterable)
True
>>> isinstance(100,Iterable)
False

4.2 迭代器

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

可以使用 isinstance() 判断一个对象是否是 Iterator 对象:

>>> from collections import Iterator
>>> isinstance([],Iterator)
False
>>> isinstance({},Iterator)
False
>>> isinstance('hello',Iterator)
False
>>> isinstance((x for x in range(5)),Iterator)
True
>>> isinstance(100,Iterator)
False

4.3 iter() 函数

生成器都是 Iterator 对象,但 list 、 dict 、 str 虽然是 Iterable ,却不是 Iterator 。

list 、 dict 、 str 等 Iterable 变成 Iterator 可以使用 iter() 函数:

>>> from collections import Iterator
>>> isinstance([1,2,3,4],Iterator)
False
>>> isinstance(iter([1,2,3,4]),Iterator)
True
>>> L = iter([1,2,3,4])
>>> L
<list_iterator object at 0x000001D71021F780>
>>> next(L)
1
>>> next(L)
2

5. 闭包

函数调用

def test1():
    print("--- in test1 func----")
test1()     # 调用函数
ret = test1     # 引用函数,ret和test1指向同一对象
print(id(ret))
print(id(test1))
ret()       # 通过引用调用函数
'''
--- in test1 func----
1900388193688
1900388193688
--- in test1 func----
'''

闭包

def test(number):   # 定义一个函数
    # 在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,
    # 那么将这个函数以及用到的一些变量称之为闭包
    def test_in(number_in):
        print("in test_in 函数, number_in is %d"%number_in)
        return number+number_in
    #其实这里返回的就是闭包的结果
    return test_in
# 给test函数赋值,这个20就是给参数number
ret = test(20)
# 注意这里的100其实给参数number_in
print(ret(100))
# 注意这里的200其实给参数number_in
print(ret(200))

'''
in test_in 函数, number_in is 100
120
in test_in 函数, number_in is 200
220
'''

def test(number):
    print("------1------")
    def test_in(number_in):
        print("------2------")
        print("in test_in 函数, number_in is %d"%number_in)
        return number+number_in
    print("------3------")
    return test_in

'''
>>> ret = test(20)
------1------
------3------
>>> ret(100)
------2------
in test_in 函数, number_in is 100
120
'''

python进阶 随笔 第1张

闭包可以简化函数的调用,如上若a,b的值不变,第一个函数可以只传参数x,而第二个函数每次调用时则要传a,b,x三个参数

由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存

 6. 装饰器

定义:装饰器本质是函数,用于装饰其他函数,就是为其他函数添加附加功能

原则:1. 不能修改被装饰函数的源代码

           2. 不能修改被装饰的函数的调用方式

实现装饰器的知识储备:

函数即“变量”:函数体存在内存中,是一个变量,函数名指向函数体,是一个变量名,当函数体没有变量指向时,解析器会将函数体回收

def bar():
    print('in the bar')
def foo():
    print('in the foo')
    bar()
foo()
'''
in the foo
in the bar
'''

def foo():
    print('in the foo')
    bar()
def bar():
    print('in the bar')
foo()
'''
in the foo
in the bar
'''

def foo():
    print('in the foo')
    bar()
foo()
def bar():
    print('in the bar')
'''
in the foo
Traceback (most recent call last):
NameError: name 'bar' is not defined
'''

高阶函数

  a. 把一个函数名当作实参传给另一个函数(在不修改被装饰函数源代码的情况下为其添加功能)

 

import time
def bar():
    time.sleep(3)
    print('in the bar')
#bar()
def test1(func):
    start_time=time.time()
    func()
    stop_time=time.time()
    print('the func run time is %s' %(stop_time-start_time))
test1(bar)    #为bar函数添加了计算函数运行时间的功能,但改变了函数的调用方式

'''
in the bar
the func run time is 3.0221872329711914
'''

 

b. 返回值中包含函数名(不修改函数的调用方式)

 

import time
def bar():
    time.sleep(3)
    print('in the bar')
def test2(func):
    print(func)    
    return func    #返回函数内存地址
bar=test2(bar)
bar()    #没改变函数的调用方式,但为bar函数添加了test2中打印函数内存地址的功能

'''
<function bar at 0x000001B84932FBF8>
in the bar
'''

 

c. 嵌套函数:函数内部再定义函数

def foo():
    print('in the foo')
    def bar():
        print('in the bar')
    bar()
foo()

'''
in the foo
in the bar
'''

装饰器的使用:@装饰器函数名

 

import time
def timer(func):
    def deco():
        start_time=time.time()
        func()
        stop_time=time.time()
        print('the func run time is %s' %(stop_time-start_time))
    return deco
@timer        # 相当于test1=timer(test1)
def test1():
    time.sleep(3)
    print('in the test1')
#test1=timer(test1)
test1()

'''
in the test1
the func run time is 3.0173559188842773
'''

 

函数参数问题:

import time
def timer(func):
    def deco(*args,**kwargs):
        start_time=time.time()
        func(*args,**kwargs)
        stop_time=time.time()
        print('the func run time is %s' %(stop_time-start_time))
    return deco
@timer
def test1():
    time.sleep(3)
    print('in the test1')
@timer
def test2(name,age):
    time.sleep(1)
    print('test2:',name,age)
test1()
test2('goku',12)

 

 装饰器原理:

def w1(func):
    def inner():
        print("----验证----")
        func()
    return inner

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

f1 = w1(f1)
f1()

'''
----验证----
-----f1----
'''
def w1(func):
    def inner():
        print("---验证权限---")
        func()
    return inner
@w1     # 等价于 f1 = w1(f1),为函数添加验证权限的功能
def f1():
    print("---f1---")
@w1
def f2():
    print("---f2---")
f1()
f2()

'''
---验证权限---
---f1---
---验证权限---
---f2---
'''

当函数被两个装饰器装饰的情况:

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

def makeItalic(fn):
    def wrapped():
        return "<i>" + fn() + "</i>"
    return wrapped
# 装饰器装饰的是函数,当有两个装饰器时,
# 先用 makeItalic 装饰函数,再用 makeBold 装饰
@makeBold   
@makeItalic
def test():
    return "hello world"
print(test())

'''
<b><i>hello world</i></b>
'''

装饰器的执行顺序:

def w1(func):
    print("---正在装饰1----")
    def inner():
        print("---正在验证权限1----")
        func()
    return inner
def w2(func):
    print("---正在装饰2----")
    def inner():
        print("---正在验证权限2----")
        func()
    return inner
@w1
@w2
def f1():
    print("---f1---")
f1()

'''
---正在装饰2----
---正在装饰1----
---正在验证权限1----
---正在验证权限2----
---f1---
'''

使用装饰器对无参数的函数进行装饰时的执行顺序

 

def func(functionName):
    print("---func---1---")
    def func_in():
        print("---func_in---1---")
        functionName()
        print("---func_in---2---")
    print("---func---2---")
    return func_in
@func
def test():
    print("----test----")
test()
'''
---func---1---
---func---2---
---func_in---1---
----test----
---func_in---2---
'''

使用装饰器对有参数的函数进行装饰

 

 1 def func(functionName):
 2     print("---func---1---")
 3     def func_in(a, b):# 如果a,b 没有定义,那么会导致13行的调用失败
 4         print("---func_in---1---")
 5         functionName(a, b)
 6         # 如果没有把a,b当做实参进行传递,那么会导致调用11行的函数失败
 7         print("---func_in---2---")
 8     print("---func---2---")
 9     return func_in
10 @func
11 def test(a, b):
12     print("----test-a=%d,b=%d---"%(a,b))
13 test(11,22)
14 '''
15 ---func---1---
16 ---func---2---
17 ---func_in---1---
18 ----test-a=11,b=22---
19 ---func_in---2---
20 '''

 

使用装饰器对有不定长参数的函数进行装饰

 

def func(functionName):
    print("---func---1---")
    def func_in(*args, **kwargs): # 采用不定长参数的方式满足所有函数需要参数以及不需要参数的情况
        print("---func_in---1---")
        functionName(*args, **kwargs) # 这个地方,需要写*以及**,如果不写的话,那么args是元组,而kwargs是字典
        print("---func_in---2---")
    print("---func---2---")
    return func_in
@func
def test(a, b, c):
    print("----test-a=%d,b=%d,c=%d---"%(a,b,c))
@func
def test2(a, b, c, d):
    print("----test-a=%d,b=%d,c=%d,d=%d---"%(a,b,c,d))
test(11,22,33)
test2(44,55,66,77)
'''
---func---1---
---func---2---
---func---1---
---func---2---
---func_in---1---
----test-a=11,b=22,c=33---
---func_in---2---
---func_in---1---
----test-a=44,b=55,c=66,d=77---
---func_in---2---
'''

 

使用装饰器对有返回值的函数进行装饰

 

def func(functionName):
    print("---func---1---")
    def func_in():
        print("---func_in---1---")
        functionName()
        print("---func_in---2---")
    print("---func---2---")
    return func_in
@func
def test():
    print("----test----")
    return "haha"
ret = test()
print("test return value is %s"%ret)
'''
---func---1---
---func---2---
---func_in---1---
----test----
---func_in---2---
test return value is None
'''

 

 1 def func(functionName):
 2     print("---func---1---")
 3     def func_in():
 4         print("---func_in---1---")
 5         ret = functionName()   # 保存返回来的haha
 6         print("---func_in---2---")
 7         return ret   # 把haha返回到14行处的调用
 8     print("---func---2---")
 9     return func_in
10 @func
11 def test():
12     print("----test----")
13     return "haha"
14 ret = test()
15 print("test return value is %s"%ret)
16 '''
17 ---func---1---
18 ---func---2---
19 ---func_in---1---
20 ----test----
21 ---func_in---2---
22 test return value is haha
23 '''

通用装饰器:对通用装饰器应满足三个条件,内部函数参数为(*args, **kwargs) ,返回函数参数为(*args, **kwargs) ,内部函数有返回值

def func(functionName):
    def func_in(*args, **kwargs):
        print("-----记录日志-----")
        ret = functionName(*args, **kwargs)
        return ret
    return func_in

@func
def test1():
    print("----test1----")
    return "haha"
@func
def test2():
    print("----test2---")
@func
def test3(a):
    print("-----test3--a=%d--"%a)

ret = test1()
print("test return value is %s"%ret)
a = test2()
print("test2 return value is %s"%a)
test3(11)

'''
-----记录日志-----
----test1----
test return value is haha
-----记录日志-----
----test2---
test2 return value is None
-----记录日志-----
-----test3--a=11--
'''

 7. 其他

7.1 import 导入模块

import搜索路径:导入模块时会从sys.path的目录里按顺序依次查找要导入的模块文件

python进阶 随笔 第2张

添加导入模块路径:

python进阶 随笔 第3张

python进阶 随笔 第4张

重新导入模块问题:

编写reload_test.py,运行

python进阶 随笔 第5张

python进阶 随笔 第6张

修改reload_test.py

python进阶 随笔 第7张

修改后重新运行或再次导入运行,结果还是不变

python进阶 随笔 第8张

采用reload方法重新导入即可得到修改后的结果

python进阶 随笔 第9张

循环导入问题:

a.py

python进阶 随笔 第10张

b.py

python进阶 随笔 第11张

模块a,b相互导入时会报错

python进阶 随笔 第12张

 7.2 作用域

 局部变量、全局变量 locals(),globals()

>>> A = 100
>>> B = 200
>>> def test():
...     a = 11
...     b = 22
...     print(locals())
...
>>> test()
{'a': 11, 'b': 22}
>>> globals()
{'__loader__': <class '_frozen_importlib.BuiltinImporter'>, 'B': 200, '__spec__': None, 'test': <function test at 0x0000025142107F28>, '__package__': None, '__doc__': None, '__builtins__': <module 'builtins' (built-in)>, 'A': 100, '__name__': '__main__'}

LEGB规则:Python 使用 LEGB 的顺序来查找一个符号对应的对象

locals > enclosing function > globals > builtins

  • locals,当前所在命名空间(如函数、模块),函数的参数也属于命名空间内的变量
  • enclosing,外部嵌套函数的命名空间(闭包中常见)
  • globals,全局变量,函数定义所在模块的命名空间
  • builtins,内建模块的命名空间
abs = 100 # globals
def test1():
    abs = 200 # enclosing function
    def test2():
        abs = 300 # locals
        print(abs)
    return test2
ret = test1()
ret()

'''
300
'''

abs = 100 # globals
def test1():
    abs = 200 # enclosing function
    def test2():
        # abs = 300 # locals
        print(abs)
    return test2
ret = test1()
ret()

'''
200
'''

abs = 100 # globals
def test1():
    # abs = 200 # enclosing function
    def test2():
        # abs = 300 # locals
        print(abs)
    return test2
ret = test1()
ret()

'''
100
'''

# abs = 100 # globals
def test1():
    # abs = 200 # enclosing function
    def test2():
        # abs = 300 # locals
        print(abs)
    return test2
ret = test1()
ret()

'''
<built-in function abs>
'''

7.3  == 和 is 的区别

Is  是比较两个引用是否指向同一个对象(引用比较)

== 是比较两个对象是否相等

>>> a = [11,22,33]
>>> b = [11,22,33]
>>> a == b
True
>>> a is b
False
>>> c = a
>>> a == c
True
>>> x = 100
>>> y = 100
>>> x == y
True
>>> x is y
True
>>> x = 10000
>>> y = 10000
>>> x == y
True
>>> x is y
False
>>> x = 257
>>> y = 257
>>> x is y
False
>>> x = 256
>>> y = 256
>>> x is y
True
>>> x = -5
>>> y = -5
>>> x is y
True
>>> x = -6
>>> y = -6
>>> x is y
False

注意:整数在程序中的使用非常广泛,Python为了优化速度,使用了小整数对象池, 避免为整数频繁申请和销毁内存空间。Python 对小整数的定义是 [-5, 257) 这些整数对象是提前建立好的,不会被垃圾回收。在一个 Python 的程序中,所有位于这个范围内的整数使用的都是同一个对象。

7.4 深拷贝 浅拷贝

浅拷贝只拷贝引用而没有拷贝内容

>>> a = [11,22,33]
>>> b = a
>>> b
[11, 22, 33]
>>> id(a)
140121014271944
>>> id(b)
140121014271944
>>> b.append(44)
>>> a
[11, 22, 33, 44]

深拷贝是对一个对象所有层次的拷贝

>>> import copy
>>> a = [11,22,33]
>>> b = copy.deepcopy(a)
>>> b
[11, 22, 33]
>>> id(a)
139992231370056
>>> id(b)
139992231369992
>>> a.append(44)
>>> a
[11, 22, 33, 44]
>>> b
[11, 22, 33]

copy 与 deepcopy 的区别:

>>> a = [11,22,33]
>>> b = [44,55,66]
>>> c = [a,b]
>>> d = c
>>> import copy
>>> e = copy.deepcopy(c)
>>> id(c)
140313120705224
>>> id(d)
140313120705224
>>> id(e)
140313241857160
>>> a.append(44)
>>> c    
[[11, 22, 33, 44], [44, 55, 66]]
>>> e
[[11, 22, 33], [44, 55, 66]]

deepcopy 如下图

python进阶 随笔 第13张

>>> import copy
>>> a = [1,2,3]
>>> b = [4,5,6]
>>> c = [a,b]
>>> d = copy.copy(c)
>>> id(c)
139808598588552
>>> id(d)
139808477437448
>>> a.append(4)
>>> c
[[1, 2, 3, 4], [4, 5, 6]]
>>> d
[[1, 2, 3, 4], [4, 5, 6]]

copy 如下图

python进阶 随笔 第14张

元组的copy问题:列表和元组的copy.copy()处理方式不一样

>>> a = [1,2,3]
>>> b = [4,5,6]
>>> c = (a,b)
>>> import copy
>>> d = copy.copy(c)
>>> e = copy.deepcopy(c)
>>> id(c)
140600696497992
>>> id(d)
140600696497992
>>> id(e)
140600696417608
>>> a.append(8)
>>> c[0]
[1, 2, 3, 8]
>>> e[0]
[1, 2, 3]

元组copy如下图

python进阶 随笔 第15张

 7.6 私有化

xx

公有变量(public)

_x 

单前置下划线,私有化属性或方法,from somemodule import *(包括双前置下划线)禁止导入,类对象和子类可以访问(protect)

__xx

双前置下划线,避免与子类中的属性命名冲突,无法在外部直接访问(名字重整所以访问不到),父类中属性名为__名字的,子类不继承,子类不能访问,如果在子类中向__名字赋值,那么会在子类中定义的一个与父类相同名字的属性(private)

__xx__

 双前后下划线,用户名字空间的魔法对象或属性。例如:__init__ , __ 不要自己发明这样的名字

xx_

单后置下划线,用于避免与Python关键词的冲突

 

# test.py
num = 100
_num = 200
__num = 300


>>> from test import *
>>> num
100
>>> _num
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name '_num' is not defined
>>> __num
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name '__num' is not defined
>>> import test
>>> test.num
100
>>> test._num
200
>>> test.__num
300
# test.py
class Person(object):
    def __init__(self,name = 'haha',score = 80):
        self._name = name
        self.__score = score
class Student(Person):
    pass

>>> from test import *
>>> Person._name
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'Person' has no attribute '_name'
>>> Person.__score
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'Person' has no attribute '__score'
>>> p = Person('hanmeimei',88)
>>> p._name
'hanmeimei'
>>> p.__score   # 私有属性,外部不能访问
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Person' object has no attribute '__score'
>>> p._Person__score    # 可通过 _Class__object 访问私有属性
88
>>> s = Student('lilei',60)
>>> s._name     # 子类可以访问
'lilei'
>>> s.__scoer       # 子类不能访问
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute '__scoer'
>>> s._Student__score
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute '_Student__score'
>>> s._Person__score
60

property的使用:将方法进行封装

方式一

class Test(object):
    def __init__(self):
        self.__num = 100
    def setNum(self,newNum):
        print("---setter---")
        self.__num = newNum
    def getNum(self):
        print("---getter---")
        return self.__num
    num = property(getNum,setNum)
t = Test()
t.num = 200 # 相当于调用 t.setNum(200)
print(t.num) # 相当于调用 t.getNum()

'''
---setter---
---getter---
200
'''

方式二

class Test(object):
    def __init__(self):
        self.__num = 100
    @property
    def num(self):
        print("---getter---")
        return self.__num
    @num.setter
    def num(self,newNum):
        print("---setter---")
        self.__num = newNum
t = Test()
t.num = 200 # 相当于调用 t.setNum(200)
print(t.num) # 相当于调用 t.getNum()

'''
---setter---
---getter---
200
'''
class Student(object):

    @property
    def score(self):
        return self._score

    @score.setter
    def score(self, value):
        if not isinstance(value, int):
            raise ValueError('score must be an integer!')
        if value < 0 or value > 100:
            raise ValueError('score must between 0 ~ 100!')
        self._score = value

7.7 垃圾回收

1) 小整数 [-5,257) 共用对象,常驻内存

 python进阶 随笔 第16张

2)单个字符共用对象,常驻内存

 python进阶 随笔 第17张

3)单个单词,不可修改,默认开启intern机制,共用对象,引用计数为0,则销毁 (python2)

python进阶 随笔 第18张

python进阶 随笔 第19张

4)字符串(含有空格),不可修改,没开启intern机制,不共用对象,引用计数为0,销毁 

 python进阶 随笔 第20张

5)大整数不共用内存,引用计数为0,销毁 

 python进阶 随笔 第21张

6)数值类型和字符串类型在 Python 中都是不可变的,这意味着你无法修改这个对象的值,每次对变量的修改,实际上是创建一个新的对象

 python进阶 随笔 第22张

python采用的是引用计数机制为主,标记-清除和分代收集两种机制为辅(针对循环引用)的策略

1、导致引用计数+1的情况

  • 对象被创建,例如a=23
  • 对象被引用,例如b=a
  • 对象被作为参数,传入到一个函数中,例如func(a)
  • 对象作为一个元素,存储在容器中,例如list1=[a,a]

2、导致引用计数-1的情况

  • 对象的别名被显式销毁,例如del a
  • 对象的别名被赋予新的对象,例如a=24
  • 一个对象离开它的作用域,例如f函数执行完毕时,func函数中的局部变量(全局变量不会)
  • 对象所在的容器被销毁,或从容器中删除对象

3、查看一个对象的引用计数

 python进阶 随笔 第23张

可以查看a对象的引用计数,但是比正常计数大1,因为调用函数的时候传入a,这会让a的引用计数+1

 7.7 内建属性

python进阶 随笔 第24张

__getattribute__ 访问对象属性时触发

# 未重写__getattribute__方法时
class Course(object):
    def __init__(self,subject1):
        self.subject1 = subject1
        self.subject2 = 'cpp'
s = Course("python")
print(s.subject1)
print(s.subject2)

'''
python
cpp
'''

# 重写__getattribute__方法
class Course(object):
    def __init__(self,subject1):
        self.subject1 = subject1
        self.subject2 = 'cpp'
    # 属性访问拦截器,打印log
    def __getattribute__(self,obj):
        if obj == 'subject1':
            print('subject1_log')
            return 'python-python'
        else:
            return object.__getattribute__(self,obj)
s = Course("python")
print(s.subject1)
print(s.subject2)

'''
subject1_log
python-python
cpp
'''

# 注释else,访问非subject1属性将返回None
class Course(object):
    def __init__(self,subject1):
        self.subject1 = subject1
        self.subject2 = 'cpp'
    # 属性访问拦截器,打印log
    def __getattribute__(self,obj):
        if obj == 'subject1':
            print('subject1_log')
            return 'python-python'
        #else:
        #    return object.__getattribute__(self,obj)
s = Course("python")
print(s.subject1)
print(s.subject2)

'''
subject1_log
python-python
None
'''

class Course(object):
    def __init__(self,subject1):
        self.subject1 = subject1
        self.subject2 = 'cpp'
    def __getattribute__(self,obj):    # 属性访问时拦截器,打log
        print("====1>%s" %obj)
        if obj == 'subject1':
            print('log_subject1')
            return 'python-python'
        else:
            temp = object.__getattribute__(self,obj)
            print("====2>%s" %str(temp))
            return temp
    def show(self):
        print('hello......')
s = Course("python")
print(s.subject1)
print(s.subject2)
s.show()

'''
====1>subject1
log_subject1
python-python
====1>subject2
====2>cpp
cpp
====1>show
====2><bound method Course.show of <__main__.Course object at 0x7fa4de183a20>>
hello......
'''

__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  # 会让程序死掉

'''
(RecursionError: maximum recursion depth exceeded while calling a 
Python object)
原因是:当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
'''

7.8 内建方法

Build-in Function,启动python解释器,输入dir(__builtins__), 可以看到很多python解释器启动后默认加载的属性和函数,这些函数称之为内建函数, 这些函数因为在编程时使用较多,cpython解释器用c语言实现了这些函数,启动解释器时默认加载

range(start, stop[, step])

# python3
>>> from collections import Iterable
>>> r = range(10)
>>> r
range(0, 10)
>>> isinstance(r,Iterable)
True
>>> from collections import Iterator
>>> isinstance(r,Iterator)
False
>>> r = iter(r)
>>> r
<range_iterator object at 0x7f538bfc9450>
>>> isinstance(r,Iterator)
True
>>> next(r)
0
>>> next(r)
1

# python2
>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> xrange(10)
xrange(10)

map()

>>> ret = map(lambda x:x*x, [1,2,3])
>>> ret
<map object at 0x7f5384b65908>
>>> for item in ret:
...     print(item)
... 
1
4
9
>>> ret = map(lambda x,y:x+y, [1,2,3],[4,5,6])
>>> ret
<map object at 0x7f538bf0ef60>
>>> for item in ret:
...     print(item)
... 
5
7
9
>>> def func(x,y):
...     return x + y
... 
>>> list1 = [1,2,3]
>>> list2 = [4,5,6]
>>> list3 = map(func,list1,list2)
>>> list3
<map object at 0x7f53849476d8>
>>> for item in list3:
...     print(item)
... 
5
7
9

filter()

filter函数会对序列参数sequence中的每个元素调用function函数,最后返回的结果包含调用结果为True的元素,返回值的类型和参数sequence的类型相同

 

>>> f = filter(lambda x:x%2, [1,2,3,4])
>>> f
<filter object at 0x7f53849479e8>
>>> for item in f:
...     print(item)
... 
1
3
>>> f = filter(None, [1,2,3])
>>> for item in f:
...     print(item)
... 
1
2
3

 

sorted()

>>> sorted([3,5,3,2,77,44,54])
[2, 3, 3, 5, 44, 54, 77]
>>> sorted(['n','c','m','b'])
['b', 'c', 'm', 'n']
>>> sorted([4,2,6,'a','m'])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'str' and 'int'
>>> sorted([3,65,32,77,23],reverse=1)
[77, 65, 32, 23, 3]
>>> sorted((3,4,1))                  
[1, 3, 4]

reduce(function, sequence[, initial])

reduce(在python3中已被移除)依次从sequence中取一个元素,和上一次调用function的结果做参数再次调用function

  • function:该函数有两个参数
  • sequence:序列可以是str,tuple,list
  • initial:固定初始值

 

>>> from functools import reduce
>>> reduce(lambda x,y:x+y, [1,2,3])
6
>>> reduce(lambda x,y:x+y, [1,2,3], 4)
10
>>> reduce(lambda x,y:x+y, ['aa','bb','cc'],'dd')
'ddaabbcc'

7.9 集合set

集合与之前列表、元组类似,可以存储多个数据,但是这些数据是不重复的

python进阶 随笔 第25张

 集合运算

python进阶 随笔 第26张

 

扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄