Python知识点总结

常见知识点

自省

自省,也叫反射,是运行时判断一个对象类型的能力。用来检查某些事务以确定它是什么、知道什么和它能做什么。相关的方法如下:

  • hasattr(object,name)检查对象是否有name属性,Boolean

    1
    2
    3
    class Hi(object):
    a = 0
    hasattr(Hi,'a')
  • getattr(object,name,default)获取对象的name属性

    1
    2
    3
    class Hi(object):
    a = 0
    getattr(Hi,'a')
  • setattr(object,name,default) 给对象设置name属性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    >>>class A(object):
    ... bar = 1
    ...
    >>> a = A()
    >>> getattr(a, 'bar') # 获取属性 bar 值
    1
    >>> setattr(a, 'bar', 5) # 设置属性 bar 值
    >>> a.bar
    5
  • delattr(object,name)给对象删除name属性

    1
    2
    3
    4
    5
    6
    7
    class Coordinate:
    z = 0
    point1 = Coordinate()
    delattr(Coordinate, 'z')
    print('z = ',point1.z)

    AttributeError: 'Coordinate' object has no attribute 'z'
  • dir([object]) 获取对象大部分的属性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class Coordinate:
    z = 0
    point1 = Coordinate()
    dir(Coordinate)

    ['__class__',
    '__delattr__',
    '__dict__',
    '__dir__',
    ...
    'z']
  • isinstance(name,object)检查name是不是object对象

    1
    2
    3
    >>>a = 2
    >>> isinstance (a,int)
    True
  • type(object) 查看对象的类型 >>> type(1) <type 'int'>

  • callable(object)判断对象是否是可调用对象

    1
    2
    3
    4
    5
    >>> def add(a, b):
    ... return a + b
    ...
    >>> callable(add) # 函数返回 True
    True

*args和**kwargs

args可以被打包成tuple,**kwargs被打包成dict。 args示例:

1
2
3
4
5
6
7
8
9
def print_multiple_args(*args):
print(type(args), args)

print_multiple_args('a', 'b', 'c')
# 通过将列表前加*打包成关键字参数,指明了接收值参数必须是*args
print_multiple_args(*['a', 'b', 'c'])

<class 'tuple'> ('a', 'b', 'c')
<class 'tuple'> ('a', 'b', 'c')

**kwargs示例:

1
2
3
4
5
6
def print_multiple_args(**kwargs):
print(type(kwargs), kwargs)
# 给字典前加**打包成关键字参数,指明接收值的参数必须是**kwargs
print_multiple_args(**{'foo':'bar'})

<class 'dict'> {'foo': 'bar'}

异常代码示例

1
2
3
4
5
6
7
8
try:
# func # 可能会抛出异常的代码
except (Exception1, Exception2) as e: # 可以捕获多个异常并处理
# 异常处理的代码
else:
# pass # 异常没有发生的时候代码逻辑
finally:
pass # 无论异常有没有发生都会执行的代码,一般处理资源的关闭和释放

GIL

GIL,解释器中一种线程同步的方式。

每个解释器都有一个GIL,直接作用是限制单个解释器进程中多线程的并行执行,使即使在多核处理器上对于单个解释器来说,同一时刻运行的线程仅限一个。同时造成的问题就是:在一个解释器进程中通过多线程的方式无法利用多核处理器来实现真正的并行。因此,Python的多线程是伪多线程,无法利用多核资源,同一时刻只能一个线程在真正的运行。

迭代器和生成器

迭代器

1660724977831.png

迭代器是一种对象,该对象包含值的可计数数字,意味着可以遍历所有值。list、tuple、dict、set都是可迭代的对象。

1
2
3
4
5
6
7
8
9
10
mytuple = ("apple", "banana", "cherry")
myit = iter(mytuple)

print(next(myit))
print(next(myit))
print(next(myit))

apple
banana
cherry

要把对象或类创建为迭代器,必须为对象实现iter()和next()方法。

生成器

生成器,一遍循环一边计算的机制,不会像迭代器一样占用大量内存,只会在使用next()函数时才会生成下一个变量。

创建生成器:

  1. 把列表的[]改为()
  2. 如果函数中包含yield关键字,那么调用函数时就会创建一个生成器对象

工作原理:通过重复调用next()方法,直到捕获一个异常。

闭包

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

1
2
3
4
5
6
7
8
9
10
11
12

def addx(x):
def adder(y):
return x + y
return adder

c = addx(8)
print(type(c))
print(c.__name__)
print(c(10))
<class 'function'>
adder

classmethod和staticmethod区别

  • 都可以通过Class.method()的方式调用
  • classmethod第一个参数是cls,可以引用类变量
  • staticmethod使用起来和普通函数一样,只不过放在类里去组织,完全可以放在类之外
  • classmethod是为了使用类变量

newinit的区别

new

new方法是一个内置的静态方法,主要作用:

  1. 在内存中为对象分配空间
  2. 返回对象的引用,将引用作为第一个参数传递给init方法

重写new方法的代码是固定的,只能 return super().__new__(cls) 。否则Python解释器到不到分配了空间的对象引用,就不会调用对象的初试方法

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class MusicPlayer(object):

def __new__(cls, *args, **kwargs):
# 如果不返回任何结果,
return super().__new__(cls)

def __init__(self):
print("初始化音乐播放对象")

player = MusicPlayer()

print(player)

初始化音乐播放对象
<__main__.MusicPlayer object at 0x1108d38e0>
  • new方法是静态方法,init方法是实例方法
  • new方法会返回一个床的实例,init方法什么都不返回
  • 只有在new方法返回一个cls的实例后面的init方法才能被调用

call

call方法使类实例对象可以像调用普通函数那样,以”对象名()“的形式使用。

1
2
3
4
5
6
7
8
9
class CLanguage:
# 定义__call__方法
def __call__(self,name,add):
print("调用__call__()方法",name,add)

clangs = CLanguage()
clangs("foo","bar")

调用__call__()方法 foo bar

深拷贝和浅拷贝

深拷贝 deepcopy()是将一个对象拷贝到另一个对象中,意味着如果对一个对象的拷贝做出改变时,不会影响原对象。其本质是对象的一次序列化和一次返回序列化。
可能遇到的问题:

  1. 一个对象如果直接或简介引用了自身,会导致无休止的递归拷贝。解决,可以通过memo字典来保存已经拷贝过的对象。
  2. 深拷贝可能对原本设计为多个对象共享的数据也进行拷贝。解决,通过pickle函数来定制指定类型对象的拷贝行为。

浅拷贝 copy() 是将一个对象的引用拷贝到另一个对象上,如果在拷贝中改动,则会影响到原对象。

1
2
3
4
5
6
7
8
9
10
import copy 

a = [1,2,3,4,5]

b = copy.deepcopy(a)
a.append(6)
c = copy.copy(a)
print(a,b,c)

[1, 2, 3, 4, 5, 6] [1, 2, 3, 4, 5] [1, 2, 3, 4, 5, 6]

单例模式

为了确保某一个类只有一个实例存在。

实现方式

模块,Python模块是天然的单例模式。当模块在第一次导入时会生成.pyc文件,当第二次导入时就会直接加载.pyc文件。因此只需要把相关函数和数据定义在一个模块中,就可以获得一个单例对象。

1
2
3
4
5
6
Class Mysingleton:
def foo(self):
pass
singleton_obj=Mysingleton()
# 保存文件,需要使用时直接在其他文件导入次文件中的对象即可。
from mysingleton import singleton_obj

new方法,实例化一个对象时,都是先执行类的new方法,实例化对象,然后再执行init方法,对这个对象初始化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Test():
instance = None
def __new__(cls,*args,**kwargs):
print('new一个对象')
if cls.instance == None:
cls.instance = object.__new__(cls)
return cls.instance
else:
return cls.instance
def __init__(self):
print('初始化对象')
c1 = Test()
c2 = Test()
c3 = Test()

print(c1)
print(c2)
print(c3)

new一个对象
初始化对象
new一个对象
初始化对象
new一个对象
初始化对象
<__main__.Test object at 0x110ad12e0>
<__main__.Test object at 0x110ad12e0>
<__main__.Test object at 0x110ad12e0>

结果可知实例化三个对象内存地址都是一样的,说明都是同一个对象。

装饰器,任何一个类使用了该装饰器就会变成一个单例模式的类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def single_class(cls):
instance = dict()
def single(*args,**kwargs):
if cls not in instance:
instance[cls] = cls(*args,**kwargs)
return instance[cls]
else:
return instance[cls]
return single

@single_class
class Test():
pass

c1 = Test()
c2 = Test()
print(c1)
print(c2)

<__main__.Test object at 0x1109172b0>
<__main__.Test object at 0x1109172b0>

装饰器将所有的对象都存储到字典里,如果之前创建过的对象就直接返回,否则就new一个。

单例模式的优点:

  1. 节约内存,实例化N个对象都实际指向同一个内存地址
  2. 多个地方创建的实例可以属性直接使用

Python解释器

  • CPython,从官网下载下的就是C语言开发的Python,所以叫CPython,使用最广
  • IPython,是基于CPython之上的一个交互式解释器
  • PyPy,采用JIT技术,对Python代码动态编译,但和CPython执行结果可能不同
  • Jython,运行在Java平台上的Python解释器
  • IronPython,运行在.net平台的Python

Python是如何实现内存管理的

Python提供了自动化的内存管理,也就是说内存空间的分配与释放都是由Python解释器运行时自动进行的。内存管理有三个关键点:

  1. 引用计数
  2. 标记清理
  3. 分代收集

引用计数,每个对象其实就是PyObject结构体,内部有一个ob——refcnt的引用计数器成员变量。程序在执行过程中,该变量会随之更新并反应引用有多少个变量引用到该对象。当该对象引用计数值为0时,它的内存就会被释放掉。

以下情况会导致引用计数加1:

  1. 对象被创建
  2. 对象被引用
  3. 对象作为参数传入到一个函数中
  4. 对象作为元素存储到一个容器中

以下情况会导致引用计数减1:

  1. 使用del
  2. 对象引用被重新赋值其他对象
  3. 一个对象离开它所在的作用域
  4. 持有该对象的容器自身被销毁
  5. 持有该对象的容器删除该对象

lambda 函数的使用

功能是利用一行代码实现的小型函数。比如:

1
2
3
4
x = lambda a : a + 10
print(x(5))

15

语法:lambda arguments : expression

------ 本文结束 ------

版权声明

Medivh's Notes by Medivh is licensed under a Creative Commons BY-NC-ND 4.0 International License.
Medivh创作并维护的Medivh's Notes博客采用创作共用保留署名-非商业-禁止演绎4.0国际许可证
本文首发于Medivh 博客( http://www.mknight.cn ),版权所有,侵权必究。