zl程序教程

您现在的位置是:首页 >  后端

当前栏目

14.Python易混淆点总结

Python 总结 14 混淆
2023-09-27 14:26:28 时间


欢迎访问个人网络日志🌹🌹知行空间🌹🌹


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

dictkey必须是可哈希对象,也就是必须是不可变类型的对象,如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() 函数返回对象的唯一标识符,标识符是一个整数。

CPythonid() 函数用于获取对象的内存地址。

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 + imagej
  • 实部和虚部都是浮点数
  • 虚部后缀可以是j/J
  • 方法conjugate返回复数的共轭复数

13 模块搜索

python搜索模块的顺序是:内建模块–>当前路径–>环境中的PYTHONPATH–>python的安装路径

14.作用域

python中的四种作用域依次是:

  • Local:局部作用域,就是在函数中定义的变量
  • Enclosing:嵌套的父级函数的局部作用域,即包含此函数的上级函数的作用域
  • Global:全局变量,即模块级别定义的变量
  • Built-in:内建模块中的变量,如int/bytearray

参考资料


欢迎访问个人网络日志🌹🌹知行空间🌹🌹