# 1. __slots__
__slots__
在Python
是用来给class
指定属性的,在CPython
中__slots__
是静态类型,避免了每个类对象维护一个__dict__
,因此使用__slots__
有两个好处,一个是访问更快,一个是节省内存,由其当需要创建非常多的对象时,效果更明显。
class Base(object):
__slots__ = 'x', 'y'
b = Base()
b.x = 100
b.y = 10
print(b.__slots__) # x,y
- 使用
__slots__
的类没有__dict__
属性,无法动态的添加属性
b.z = 12 # error
"""
AttributeError: 'Base' object has no attribute 'z'
"""
- 若想在使用
__slots__
的类对象中动态添加属性,需在__slots__
中加入__dict__
class Base(object):
__slots__ = 'x', 'y', '__dict__'
b = Base()
b.z = 12 # OK
print(b.__dict__) # {z: 12}
- 子类中没有
__slots__
时会继承父类的,子类定义__slots__
会覆盖父类的
class Base(object):
__slots__ = 'x', 'y'
...
class Derived1(Base):
...
b = Derived1()
print(b.__slots__)
class Derived2(Base):
__slots__ = 'z'
b = Derived2()
print(b.__slots__)
# 2.可变与不可变数据类型
可变数据类型
- 列表list
- 字典dict
- 集合set
不可变数据类型
- 字符串str
- 元组tuple
如下代码:
tup = (1, 2, [3,4])
tup[2] += [5, 6]
上面的代码在执行到tup[2] += [5, 6]
时,会报错,tup[2]
是list
可变类型,给其追加元素不会改变id
值,但是tuple
是不可变类型,因此在执行=
赋值操作时会报错。程序执行结束后tup = (1, 2, [3, 4, 5, 6])
def fun(a=(), b=[]):
a += (1, )
b.append(1)
return a, b
fun()
print(fun())
Python的默认函数只在函数定义时被赋值一次,而不会每次调用函数时又创建新引用。这意味着,函数定义完成后,默认参数已经存在固定的内存地址了,如果使用一个可变的默认参数并对其进行改变,那么以后对该函数的调用都会改变这个可变对象,而默认参数如果是不可变对象,不存在该问题。
a = [['1', '2'] for i in range(2)]
b = [[`1`, `2`]] * 2
a[0][1] = '3'
b[0][0] = '4'
print(a, b)
# [['1', '3'], ['1', '2']], [['4', '2'], ['4', '2']]
使用列表生成式生成的列表,列表中的两个元素虽然还是列表,但其是不同的引用。
而使用*
生成的列表,两个元素指向同个地址,修改时相互影响。
sorted
和+=
使用sorted
函数进行排序会生成新的序列,生成的新序列和原序列id
必然不同。对于+=
操作,如果是可变对象,则操作前后序列的id
值不变,如果是不可变对象,则操作前后序列的id
值改变。
# 3.闭包
def fn():
t = []
i = 0
while i < 2:
t.append(lambda x: print(i * x))
i += 1
return t
for f in fn():
f(2)
# 4
# 4
函数fn
中存在闭包现象,自由变量是i
,由于Python
闭包采用延迟绑定,当调用lambda
匿名函数时,循环早就结束,此时i的值为2,所以结果的输出都是2*2
# 4.dict的key
dict
的key
必须是可哈希对象,也就是必须是不可变类型的对象,如int/float/str/tuple/frozenset
d = {}
k = 1
d[k] = 10 # ok
k = "nu"
d[k] = 10 # ok
k = (1,2)
d[k] = 10 # ok
k = frozenset((1,2))
d[k] = 10 # ok
k = [1,2]
d[k] = 10 # error
k = dict(k=1)
dict[k] = 10 # error
k = {1,2}
dict[k] = 10 # error
# 5.range的slice
a = range(100)
a[-1] # 99
a[-3] # 97
a[::3] # range(0, 100, 3)
a[2:13] # range(2, 13)
a[2-3] # 99
# 6.return
Python
中函数的return
语句不能返回多个值,当返回多个值时其实是一个tuple
类型的值
# 7.函数的*
参数
当函数的形参单独为*
时,它并不属于未匹配的位置参数,而是表示其后面的参数必须使用关键字参数进行匹配。
# a, b 都是位置参数
def f(a,b,*):
...
f(1,2,3,4)
# a都是位置参数, b 是关键字参数
def f(a,*,b):
...
f(1,2,3,4) # exception
f(1,b=4) # ok
# 8.id函数的使用
id()
函数返回对象的唯一标识符,标识符是一个整数。
CPython
中 id()
函数用于获取对象的内存地址。
a = [1, 2, 3]
b = [4, 2, 5]
print(id(a[1]))
print(id(b[1]))
上面的例子会发现两个输出的值是相同的,这是因为python中对于小整数有一个小整数的对象池,范围在[-5, 257)
之间。对于在这个范围内的整数,不会新建对象,直接从小整数对象池中取即可。
# 9.global的使用
在函数中使用全局变量时需指明global
x = 10
def add1():
x = x +1
add() # error
def add2():
global x
x = x + 1
add()
print(x) # 11
add1
中会报错,因为不使用global
关键字,x
已经变成函数中的局部变量,而x
先出现在等号右边,因此未定义先使用,所以报错。
# 10.关于python中的class
- 属性的访问控制
object
是公开属性。
_object
是保护属性,只有在类中和子类中才能访问,在模块和类外不可以使用,不能使用from module import *
来导入_object
属性。
__object
是私有属性,只有在当前类中使用,在类外访问不到。其实,在类外也能访问到,因为__object
的实际名字变成了_classnamme__object
,所以在类外可以通过_classnamme__object
访问__object
属性。
class Obj:
def __init__(self):
self.__a = 1
o = Obj()
o._Obj__a # 1
__object__
是built-in
的方法,用户不应该这样定义。
__init__
和__new__
__new__
是一个静态方法,而__init__
是一个实例方法
__new__
方法会返回一个创建的实例,而__init__
什么都不返回。
只有在__new__
返回一个cls的实例时,后面的__init__
才能被调用
当创建一个新实例时调用__new__
,初始化一个实例时用__init__
- property属性
class Dog:
def __init__(self, color):
self.__color = color
@property
def color(self):
return self.__color
@color.setter
def color(self, color):
self.__color = color
dog = Dog("red")
print(dog.color)
dog.color = "white"
print(dog.color)
@property
装饰器相当于一个get
方法,用于获取私有属性的值,为了修改私有属性的值,需要添加一个setter
,对于setter
装饰器的语法是@方法名.setter
。
# 11.string
split
方法的使用
s = " this is Tom. "
print(s.split(" "))
# ['', '', 'this', 'is', 'Tom.', '', '']
print(s.split())
# ['this', 'is', 'Tom.']
split(sep)
函数表示以sep
为分隔符切片字符串,sep = " "
,则分隔后的结果为[" ", "I", "like", "python", " "]
。不传入sep
时,sep=None
, split according to any whitespace,and discard empty strings from the result.
# 12.复数
- 表示复数的语法是
real
+image
j - 实部和虚部都是浮点数
- 虚部后缀可以是
j/J
- 方法
conjugate
返回复数的共轭复数
# 13 模块搜索
python
搜索模块的顺序是:内建模块-->当前路径-->环境中的PYTHONPATH
-->python
的安装路径
# 14.作用域
python
中的四种作用域依次是:
Local
:局部作用域,就是在函数中定义的变量Enclosing
:嵌套的父级函数的局部作用域,即包含此函数的上级函数的作用域Global
:全局变量,即模块级别定义的变量Built-in
:内建模块中的变量,如int/bytearray
等