Python3知识点杂记(一)

python相关知识点的一些小结

1、lambda 函数:

lambda函数是一个可以接收任意多个参数并返回单个表达式值的函数。lambda函数不能包含命令,它们所包含的表达式不能超过一个。
不要试图往lambda函数中塞入太多东西,如果你需要更复杂的东西,应该定义一个普通的函数。

f = lambda x, y: x*y  # 传入x,y,返回x*y的结果

f(3, 4)
12

2、深拷贝与浅拷贝

深拷贝是将对象本身复制给另一个对象,这意味着如果对对象的副本进行更改时不会影响原对象,在Python中使用deepcopy()进行深拷贝。
deepcopy()使用的是递归copy,只要拷贝的对象里面包括可变类型(列表,字典)就会重新开辟一个空间,这个空间里的不可变类型(元组、字符串、bool、数字)将是指向原对象里的不可变类型,即只是拷贝了引用,对于可变类型则会开辟空间。

浅拷贝是将对象的引用复制给另一个对象,因此,如果我们对副本进行更改,则会影响原对象。在python中,使用copy()进行浅拷贝。
copy()只拷贝一次最顶层,如果拷贝的对象是不可变类型,则指向,如果拷贝的是可变类型,则开辟一个空间。

import copy

a = [1, 2, 3, ['a', 'b', (1,2)]]  # 可变类型数据
b = copy.copy(a)
c = copy.deepcopy(a)
d = a  # <==> d ---> a,d指向a所指向的这个列表,所以它们的id是一样的(也算是浅拷贝)

print(a, id(a))
print(b, id(b))
print(c, id(c))
print(d, id(d))
# 打印结果:
# [1, 2, 3, ['a', 'b']] 2193589840008
# [1, 2, 3, ['a', 'b']] 2193589840840  对于可变类型数据,无论是深拷贝还是浅拷贝都会起初都会开辟一个空间,
# [1, 2, 3, ['a', 'b']] 2193589840072  所以它们的id都不同。
# [1, 2, 3, ['a', 'b']] 2193589840008  只有幅值语句,才会有相同的id。

print('-'*60)

print(id(a[0]), id(b[0]), id(c[0]), id(d[0]))  # 对原对象中的不可变类型,无论是深拷贝还是浅拷贝,都只是复制引用,不会开辟新的空间
print(id(a[3][1]), id(b[3][1]), id(c[3][1]), id(d[3][1]))
print(id(a[3][2]), id(b[3][2]), id(c[3][2]), id(d[3][2]))
# 打印结果:
# 1399878288     1399878288     1399878288     1399878288
# 2086484466632  2086484466632  2086484466632  2086484466632
# 2086484452424  2086484452424  2086484452424  2086484452424

print('-'*60)

print(id(a[3]), id(b[3]), id(c[3]), id(d[3]))  # 对原对象的可变类型,深拷贝则会重新开辟一个空间保存,而浅拷贝只会复制引用,即对副本里的可变类型数据进行修改会影响原对象里的内容
# 打印结果:
# 2086501946120 2086501946120 2086501946504 2086501946120
print('-'*20)
b[3].append('c')  # 对副本里的可变类型数据进行修改会影响原对象里的内容
c[3].append('d')  # 深拷贝则不会影响原对象里的内容

print(a, id(a))  # 受副本影响
print(b, id(b))
print(c, id(c))
# 打印结果:
# [1, 2, 3, ['a', 'b', (1, 2), 'c']] 2086501946248
# [1, 2, 3, ['a', 'b', (1, 2), 'c']] 2086501947016
# [1, 2, 3, ['a', 'b', (1, 2), 'd']] 2086501946312

print('-'*60)

# 这里可能会有一个疑问,a本身也是一个可变类型,那么对他浅拷贝的对象b进行修改不应该也会影响a吗?
# 其实对可变类型进行深/浅拷贝,都会开辟一个新的内存空间,画个图就很好理解了,见下图。
b.append(4)
c.append(5)

print(a, id(a))
print(b, id(b))
print(c, id(c))
[1, 2, 3, ['a', 'b', (1, 2)]] 2343085875656
[1, 2, 3, ['a', 'b', (1, 2)]] 2343085876424
[1, 2, 3, ['a', 'b', (1, 2)]] 2343085875720
[1, 2, 3, ['a', 'b', (1, 2)]] 2343085875656
------------------------------------------------------------
1399878288 1399878288 1399878288 1399878288
2343005384016 2343005384016 2343005384016 2343005384016
2343068396488 2343068396488 2343068396488 2343068396488
------------------------------------------------------------
2343085875528 2343085875528 2343085875912 2343085875528
--------------------
[1, 2, 3, ['a', 'b', (1, 2), 'c']] 2343085875656
[1, 2, 3, ['a', 'b', (1, 2), 'c']] 2343085876424
[1, 2, 3, ['a', 'b', (1, 2), 'd']] 2343085875720
------------------------------------------------------------
[1, 2, 3, ['a', 'b', (1, 2), 'c']] 2343085875656
[1, 2, 3, ['a', 'b', (1, 2), 'c'], 4] 2343085876424
[1, 2, 3, ['a', 'b', (1, 2), 'd'], 5] 2343085875720

Python3知识点杂记(一)

如果a 是不可变类型浅拷贝最初就是指向,不再像上面一样重新开辟空间了(上面a是一个可变类型)。
只要a 中含有可变类型数据深拷贝最初就会开辟一个新的空间,对原对象进行递归拷贝,遇到不可变类型就复制引用,遇到可变类型就开辟空间进行递归拷贝

a = (1,2,3, ['a', 'c'])

# 如果a 是不可变类型,浅拷贝的最初就是指向,不再像上面一样重新开辟空间了
b = copy.copy(a)  

# 只要a 中含有可变类型数据,深拷贝就会开辟一个新的空间,对原对象进行递归拷贝,遇到不可变类型就复制引用,遇到可变类型就开辟空间进行递归复制
c = copy.deepcopy(a)

print(id(a), id(b), id(c))
print(id(a[3]), id(b[3]), id(c[3]))
2343085633352 2343085633352 2343085681064
2343085863496 2343085863496 2343085858504

3、使用三元运算符

[on_true] if [expression] else [on_false]

x, y = 20, 30
big = x if x < y else y  # 如果x < y为真,则返回值为big=x,否则big=y
big
20

4、如何用Python来进行查询和替换一个文本字符串?

可以使用sub()方法来进行查询和替换,sub方法的格式为:

re.sub(pattern, repl, string, count=0, flags=0)

  • pattern:表示正则表达式中的模式字符串;
  • repl:被替换的字符串(既可以是字符串,也可以是函数);
  • string:要被处理的,要被替换的字符串;
  • count:匹配的次数, 默认是全部替换
  • flags:具体用处不详
import re

a = "abc123def123"
re.sub(repl='xxoo', string=a, pattern=r'\d+', count=1)
'abcxxoodef123'

或者使用replace进行替换

str.replace(old, new[, max])

参数

  • old – 将被替换的子字符串。
  • new – 新字符串,用于替换old子字符串。
  • max – 可选字符串, 替换不超过 max 次
a = "abc123def123"
a.replace('123', 'xxoo', 1)
'abcxxoodef123'

5、字符串的strip和split操作

s为字符串,rm为要删除的字符序列. 只能删除开头或是结尾的字符或是字符串。不能删除中间的字符或是字符串

  • s.strip(rm) 删除s字符串中开头、结尾处,位于 rm删除序列的字符
  • s.lstrip(rm) 删除s字符串中开头处,位于 rm删除序列的字符
  • s.rstrip(rm) 删除s字符串中结尾处,位于 rm删除序列的字符

当rm为空时,默认删除空白符(包括’\n’, ‘\r’, ‘\t’, ’ ')

s.split() 通过指定分隔符对字符串进行切片,如果参数 num 有指定值,则分隔 num+1 个子字符串

  • str – 分隔符,默认为所有的空字符,包括空格、换行(\n)、制表符(\t)等。
  • num – 分割次数。默认为 -1, 即分隔所有。
a = """
祝:
    大家520快乐!
    2019-5-20
"""
# a输出为:'\n祝:\n    大家520快乐!\n    2019-5-20\n'

print(a.strip())  # 默认删除首尾的空白字符
print("-"*30)

# rm删除序列,(只要这个序列里有的字符)它都会按照顺序从序列中找,然后删除(头尾),碰到序列中没有的就结束,序列中字符顺序可以不同
print(a.strip("祝:\n 025-"))
print(a.strip("0祝2\n 5-:"))
print("-"*30)
print(a.lstrip('0祝2\n 5-:'))  # 删除开头
print(a.rstrip('0祝2\n 5-:'))  # 删除结尾

print("-"*30)
print(a.split())  # 默认以空白符分割
print(a.split('0'))  # 以自定字符分割
print(a.split('0', 1))  # 分割一次
祝:
    大家520快乐!
    2019-5-20
------------------------------
大家520快乐!
    2019
大家520快乐!
    2019
------------------------------
大家520快乐!
    2019-5-20


祝:
    大家520快乐!
    2019
------------------------------
['祝:', '大家520快乐!', '2019-5-20']
['\n祝:\n    大家52', '快乐!\n    2', '19-5-2', '\n']
['\n祝:\n    大家52', '快乐!\n    2019-5-20\n']

6、如何在Python中随机化列表中的项目?

from random import shuffle

x = ['I', 'Love', 'You', '5-20','2019']
shuffle(x)

print(x)
['You', '2019', 'I', '5-20', 'Love']

7、在Python中如何使用多进制数字?

print("二进制:", bin(10))
print("八进制:", oct(10))
print("十六进制:", hex(10))
print("ASCII值:", ord('a'))
print("十进制:", int(0xa))
二进制: 0b1010
八进制: 0o12
十六进制: 0xa
ASCII值: 97
十进制: 10

8、元组的解封装

_tuple = 3,4,5  # 封装到元组中
print(_tuple)

x, y, z = _tuple  # 解封装到x,y,z中
print(x, y, z)
(3, 4, 5)
3 4 5

9、Python中, list, tuple, dict, set有什么区别, 主要应用在什么样的场景?

定义:

list:链表, 有序的项目, 通过索引进行查找, 使用方括号"[]";

tuple:元组, 元组将多样的对象集合到一起, 不能修改, 通过索引进行查找, 使用括号"()";

dict:字典, 字典是一组键(key)和值(value)的组合, 通过键(key)进行查找, 没有顺序, 使用大括号"{}";

set: 集合,无序, 元素只出现一次, 自动去重, 使用"set([])";

应用场景:

list, 简单的数据集合, 可以使用索引;

tuple, 把一些数据当做一个整体去使用, 不能修改;

dict, 使用键值和值进行关联的数据;

set, 数据只出现一次, 只关心数据是否出现, 不关心其位置;

10、a=1, b=2, 不用中间变量交换a和b的值.

两种形式: 加法或异或

a = 1
b = 2

# 最简单的方法
# a, b = b, a
# print ('a = {0}, b = {1}'.format(a,b)) 

# 加法
# a = a + b 
# b = a - b 
# a = a - b 

# print ('a = {0}, b = {1}'.format(a,b)) 

# 异或,相同为0不同为1
a = a ^ b  # 0001 ^ 0010 = 0011 -->(3)
print(a)
b = a ^ b  # 0011 ^ 0010 = 0001 -->(1)
print(b)
a = a ^ b  # 0011 ^ 0001 = 0010 -->(2)
print(a)

print ('a = {0}, b = {1}'.format(a, b))
3
1
2
a = 2, b = 1

11、连续幅值问题,从左到右

x = [0, 1]
i = 0

i, x[i] = 1, 2  # 先给i幅值,此时i为1,然后给x[i]幅值,此时x[i] = x[1],所以x[1] = 2

print(i, x)
1 [0, 2]

12、Python 里面如何生成随机数?

import random
# random.randrange(start,stop,[,step]):返回一个范围在(start,stop,step)之间的随机整数,不包括结束值。
print(random.randrange(1, 10, 2))

# random.random( ):返回0到1之间的浮点数
print(random.random())

# random.uniform(a,b):返回指定范围内的浮点数
print(random.uniform(1, 5))
1
0.9238072455944534
2.6679905796587517

13、如何在Python中实现多线程?

  • Python有一个multi-threading包,但是如果你想让multi-thread加速你的代码,那么使用它通常不是一个好主意。

  • Python有一个名为GlobalInterpreter Lock(GIL)的结构。 GIL确保只有一个“线程”可以在任何时候执行。一个线程获取GIL,做一点工作,然后将GIL传递到下一个线程。

  • 这种情况很快发生,因此对于人眼看来,您的线程似乎并行执行,但它们实际上只是轮流使用相同的CPU核心。

  • 所有这些GIL传递都增加了执行的开销。这意味着如果您想让代码运行得更快,那么使用线程包通常不是一个好主意。

14、python中函数参数传递

a = 0
b = []

def f(a):
    a = 2

def f2(b):
    b.append(1)

f(a)
f2(b)

print(a, b)
0 [1]

python 中一切皆对象,对象有两种:可更改(list,dict)和不可更改(tuple,string,number),对不可更改对象进行修改时,python会重新开辟一个空间,然后将这个对象的引用指向新的内存空间,对可更改对象则不需要,见下例:

a = 1
print(id(a))
a = 2
print(id(a))

print("-"*20)

b = []
print(id(b))
b.append(1)
print(id(b))
1399878288
1399878320
--------------------
2343085866760
2343085866760

在看函数的那个例子:将一个引用传递给函数,函数会复制一份应用,对于不可变类型,对其修改时,python会重新开辟一个空间(而且这个引用的作用域在函数里),所以修改时不会影响原变量的值,而对可变类型修改时,这个引用则指向同一个对象,修改时就会影响原值。结合他的引用地址可以更好的理解:

a = 0
b = []

print("a的初始地址", id(a))
def f(a):
    print("函数复制了a的引用,此时a指向的地址为:", id(a))
    a = 2
    print("对a进行修改后,a的引用指向的地址为:", id(a))

f(a)    

print("-"*60)

print("b的初始地址", id(b))
def f2(b):
    print("函数复制了b的引用,此时b指向的地址为:", id(a))
    b.append(1)
    print("对b进行修改后,b的引用指向的地址为:", id(a))

f2(b)
a的初始地址 1399878256
函数复制了a的引用,此时a指向的地址为: 1399878256
对a进行修改后,a的引用指向的地址为: 1399878320
------------------------------------------------------------
b的初始地址 2343085873992
函数复制了b的引用,此时b指向的地址为: 1399878256
对b进行修改后,b的引用指向的地址为: 1399878256

15、类的三种方法:类方法、实例方法,静态方法

class A():
    def foo(self, x):  # self:实例对象,所以执行a.foo('a') <==> foo(a, 'a')
        print("我是实例方法:{0}".format(x))
    
    @classmethod
    def class_foo(cls, x):  # cls:类对象,所以执行A.class_foo('b') <==> class_foo(A, 'b')
        print("我是类方法:{0}".format(x))
        
    @staticmethod
    def static_foo(x):
        print("我是静态方法:{0}".format(x))
        
a = A()
# 实例对象可以调用:实例方法、类方法、静态方法
a.foo("a")
a.class_foo('b')
a.static_foo('c')

# 类对象只能调用类方法和静态方法
A.class_foo('b')
A.static_foo('c')
A.foo('a')  # 报错:如果非要调用实例方法,解决办法:A.foo(A(), 'a')传一个实例对象进去就行
我是实例方法:a
我是类方法:b
我是静态方法:c
我是类方法:b
我是静态方法:c



---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-17-d056086df77c> in <module>()
     20 A.class_foo('b')
     21 A.static_foo('c')
---> 22 A.foo('a')  # 报错:如果非要调用实例方法,解决办法:A.foo(A(), 'a')传一个实例对象进去就行


TypeError: foo() missing 1 required positional argument: 'x'

此外,为什么使用静态方法呢?从静态方法的定义中可以看出,静态方法是不能使用类或实例属性和方法的(因为没有self和cls),静态方法只是一个独立的、单纯的函数,它仅仅托管于某个类的名称空间里,便于使用和维护。当然也可以定义在类的外部,但是这样会打乱逻辑关系,导致以后带歪维护困难!

16、类变量和实例变量

  • 类变量:类的所有实例之间共享的变量,但是实例对象只能访问,而不能修改(不能修改不可变变量,可以修改可变变量),每个实例对象访问同一个类变量时,得到的都是相同的结果。
  • 实例变量:实例之后,每个实例对象单独拥有的变量,它允许每个实例对象的实例变量的值不同。
class A():
    name = "aaa"
    def __init__(self, name):
        self.xx = name
        
a1 = A('a1')
a2 = A('a2')

print("实例对象访问类变量:", a1.name, a2.name)
print("实例对象访问实例变量:", a1.xx, a2.xx)

a1.name = "xxoo"
print("a1试图修改不可变类变量,检查a1的实例属性:", a1.__dict__)  # 其实这是并没有修改A的类变量,而是在a1实例里动态添加了一个实例属性,名字为name,这时a1就不能再访问类变量了
print("a2未对类变量修改,检查a2的实例属性:", a2.__dict__)
print(a1.name)
print(a2.name)
print(A.__dict__)

实例对象访问类变量: aaa aaa
实例对象访问实例变量: a1 a2
a1试图修改不可变类变量,检查a1的实例属性: {'name': 'xxoo', 'xx': 'a1'}
a2未对类变量修改,检查a2的实例属性: {'xx': 'a2'}
xxoo
aaa
{'__init__': <function A.__init__ at 0x000002218ACD70D0>, '__dict__': <attribute '__dict__' of 'A' objects>, '__module__': '__main__', 'name': 'aaa', '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
class A():
    _list = []
    def __init__(self, name):
        self.xx = name
        
a1 = A('a1')
a2 = A('a2')

print("实例对象访问类变量:", a1._list, a2._list)
print("实例对象访问实例变量:", a1.xx, a2.xx)

a1._list.append("xxoo")
print("a1试图修改类可变变量,检查a1的实例属性:", a1.__dict__)
print("a2未对类变量修改,检查a2的实例属性:", a2.__dict__)
print(a1._list)
print(a2._list)
print(A.__dict__)

实例对象访问类变量: [] []
实例对象访问实例变量: a1 a2
a1试图修改类可变变量,检查a1的实例属性: {'xx': 'a1'}
a2未对类变量修改,检查a2的实例属性: {'xx': 'a2'}
['xxoo']
['xxoo']
{'__init__': <function A.__init__ at 0x000002218ACD4510>, '__dict__': <attribute '__dict__' of 'A' objects>, '__module__': '__main__', '_list': ['xxoo'], '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}

其实,类变量的修改又回到了可变变量和不可变变量的引用问题,但是唯一不同的就是多了一个动态添加实例属性的概念。***

未完待续…