python进阶知识点

1. 关键字is 和 == 的区别

作用在于比较两个变量是否指向了同一个对象,is所比较的是对象,而==比较的是对象所指代的值

1
2
3
4
5
a = 'hello world'
b = 'hello world'
c = a[:]
print(id(a), id(b), id(c)) # 都指向同一个对象 ,1706679966576 1706679966576 1706679966576
print(a==b, a is b, a is c) # 都为True, True True True
1
2
3
4
5
6
>>> a = 'hello world'
>>> b = 'hello world'
>>> print(a==b)
True
>>> print(a is b)
False

2. 深拷贝和浅拷贝

深拷贝指的是复制内容,单独开辟一个内存,浅拷贝指的是两个变量同时指向一个内存ID。

1
2
3
4
5
6
7
8
9
10
import copy
a = [1,2,3,4,5]
b = a #浅拷贝,a,b同时指向一个id,当其中一个修改时,另外一个也会被修改。
c = copy.deepcopy(a) #深拷贝,c单独开辟一个id,用来存储和a一样的内容。
d =a[:] #这样也是深拷贝。
e = copy.copy(a) # 当拷贝内容是可变类型时,那么就会进行深拷贝,如果是不可变类型时,那么就会进行浅拷贝
f = (1,3)
g = copy.copy(f)
print(id(a),id(b),id(c),id(d),id(e),id(f),id(g))
# 1706683021760 1706683021760 1706683112832 1706683111872 1706683034368 1706679894144 1706679894144

3. 私有化和Property

我们可以使用@property装饰器来创建只读属性,@property装饰器会将方法转换为相同名称的只读属性,可以与所定义的属性配合使用,这样可以防止属性被修改

Python 代码遵循统一的访问原则,公认的原则包括:

  • 直接暴露实例变量,应当是foo.x = 0, 而不是 foo.set_x(0)
  • if you need to wrap the accesses inside methods, for whatever reason, use @property, which preserves the access semantics. That is, foo.x = 0 now invokes foo.set_x(0).
  • 在方法或变量前加上单下划线或者双下划线只是约定为private`,但无法阻止外部对内部方法的访问和修改。
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
29
30
31
class Person:
def __init__(self, first_name):
self._first_name = first_name
self._last_name = 'gan'

# Getter函数
@property
def first_name(self):
return self._first_name

# Setter函数
@first_name.setter
def first_name(self, value):
if not isinstance(value, str):
raise TypeError('不是字符串。')
self._first_name = value

# Deleter函数
@first_name.deleter
def first_name(self):
raise AttributeError('不能删除。')
@property
def last_name(self): # #方法加入@property后,这个方法相当于一个属性,这个属性可以让用户进行使用,而且用户有没办法随意修改。
return self._last_name

a = Person('ke')
print(a.first_name)
a.first_name = 42
# TypeError: 不是字符串。
del a.first_name
# AttributeError: 不能删除。

上述三个相关联的方法,名字必须一样。

第一个方法是Getter函数,使first_name成为一个属性。

其他两个方法给first_name属性添加SetterDeleter函数。只有first_name属性被创建后,后面的装饰器@first_name.setter@first_name.deleter才能被定义。此时,可以修改

访问property时会自动触发getter、setter、deleter方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
```
参考:
- https://www.cnblogs.com/keye/p/15714098.html
- https://www.cnblogs.com/techflow/p/12747480.html

## 4. 列表生成式 与 生成器
> 生成器占用内存小,在使用的时候取值,降低CPU和内存空间,提高效率。并且一般都使用for循环进行取值。

```py
range(1,100,5) #第一个参数表示开始位,第二个参数表示结束位(不含),第三个参数表示步长,就是每5个数返回一次。
a = [i for i in range(1,10)] #列表生成式表示返回i的值,并且返回9次,每次返回的是i的值。
a = [2 for i in range(1,10)] #这里表示返回2,并且返回9次,但是每次的值都是2。
a = [i for i in range10 if i%2==0] #表示在生成式内部加入if判断,当i除以2的余数等于0的时候将数值返回。
a = [(i,j) for i in range(5) for j in range(5)] #表示将i和j的值以元组为元素的形式返回,当i循环一次的时候j循环5次,以此类推。
1
2
3
4
5
6
7
8
9
10
11
a = (i for i in range(1,10)) #将列表生成试外部的中括号改为小括号,就能将生成式转化为生成器。
print(next(a),a.__next__()) #生成器的取值方式只能使用next的方法。
def num():
a,b = 0,1
for i in range(10):
yield b #生成关键字yield,有yield的关键字的代码块就是yield的生成器。当运行到yield时代码就会停止,并返回运行结果,当在次运行时依旧是到yield停止,并返回结果。 切记:生成器只能使用next方法。
a,b = b,a+b
temp = yield b #这里并不是变量的定义,当运行到yield时就会停止,所以当运行到等号右边的时候就会停止运行,当在次使用next的时候,将会把一个None赋值给temp,因为b的值已经在上轮循环中输出。这里可以使用num().send()方法将一个新的值赋值给temp。
a = num() #将生成器赋值给变量a。
for n in a: #生成器可以使用for循环使用,并且不会出错。
print(n)

5. 迭代器

生成器是可迭代对象,迭代器不一定是生成器。并且迭代器无法回取,只能向前取值。

一个对象具有 iter 方法的才能称为可迭代对象,使用yield生成的迭代器函数,也有iter方法。凡是没有iter方法的对象不是可迭代对象,凡是没有__next__()方法的不是是生成器。(这里的方法都是魔法方法,是内置方法,可以使用dir()查看)

1
2
3
4
5
6
7
8
9
10
for i in 'a',[1,2],(3),{4}:
pass
#可以for循环的对象是可迭代对象。
a = (i for i in range(100))
#列表生成式,把中括号改为小括号就可以变为一个列表生成器,是可迭代对象。
from collections.abc import Iterable #如果想验证是否是可迭代对象,可以使用isinstance()判断是否是可迭代对象。
print(isinstance('abc',Iterable)) #判断语法

a1 = [1,2,3,4,5]
b1 = iter(a1) #使用iter()方法可以将可迭代对象转换为可迭代对象。

参考

主体框架参考:https://blog.csdn.net/qq_41853758/article/details/82853811