Python 提供丰富了内置函数,这些函数的使用频率非常用,在编程过程中熟练使用内置函数可以大大减少我们的代码逻辑和代码量。
Python 解释器内置了很多函数和类型,任何时候都能使用。这些内置函数直接使用,是不需要导入库的。
内置函数列表
|-------|------------------------------------------------------|--------------|
| - | 函数 | 功能 |
| A | abs() | 数字的绝对值 |
| | aiter() | 异步迭代器 |
| | all() | 是否全为 True |
| | any() | 是否有 True 值 |
| | anext() | 异步迭代下一项 |
| | ascii() | 转为 ASCII 字符 |
| B | bin() | 转为二进制字符串 |
| | bool() | 返回布尔值 |
| | breakpoint() | 设置断点 |
| | bytearray() | 生成字节数组 |
| | bytes() | 生成字节序列 |
| C | callable() | 对象是否可调用 |
| | chr() | Unicode 字符串 |
| | classmethod() | 方法封装成类方法 |
| | compile() | 编译为代码对象 |
| | complex() | 创建复数 |
| D | delattr() | 删除对象属性 |
| | dict() | 创建字典 |
| | dir() | 对象的所有属性和方法 |
| | divmod() | 计算商和余数 |
| E | enumerate() | 添加元素索引 |
| | eval() | 执行字符串表达式 |
| | exec() | 执行复杂代码 |
| F | filter() | 元素过滤 |
| | float() | 构造浮点数对象 |
| | format() | 格式化 |
| | frozenset() | 构建不可变集合 |
| G | getattr() | 获取对象属性 |
| | globals() | 全局变量 |
| H | hasattr() | 检测对象属性 |
| | hash() | 对象的哈希值 |
| | help() | 内置帮助 |
| | hex() | 16进制字符串 |
| I | id() | 对象标识值 |
| | input() | 获取输入字符串 |
| | int() | 构造整数 |
| | isinstance() | 实例类型检测 |
| | issubclass() | 子类检测 |
| | iter() | 生成迭代器 |
| L | len() | 对象长度 |
| | list() | 创建列表 |
| | locals() | 当前本地符号表的字典 |
| M | map() | 对序列元素应用函数处理 |
| | max() | 最大元素 |
| | memoryview() | 创建内存视图 |
| | min() | 最小元素 |
| N | next() | 迭代下一项 |
| O | object() | 空对象 |
| | oct() | 转为八进制字符串 |
| | open() | 打开文件 |
| | ord() | Unicode 码位编号 |
| P | pow() | 求n次幂 |
| | print() | 打印对象内容 |
| | property() | 修饰方法 |
| R | range() | 等差数列对象 |
| | repr() | 打印形式字符串 |
| | reversed() | 序列反向迭代器 |
| | round() | 小数舍入 |
| S | set() | 创建集合对象 |
| | setattr() | 设置对象属性 |
| | slice() | 切片对象 |
| | sorted() | 返回已排序列表 |
| | staticmethod() | 方法转换为静态方法 |
| | str() | 对象的字符形式 |
| | sum() | 求和 |
| | super() | 调用超类方法 |
| T | tuple() | 构建元组 |
| | type() | 对象的类型 |
| V | vars() | 对象属性字典 |
| Z | zip() | 同位元素对象组合 |
| - | import() | 导入模块 |
常用函数说明
len()
内置函数 len() 返回对象的长度(元素个数)。len(s) 中 s 实参可以是序列(如 string、bytes、tuple、list 或 range 等)或容器类型(如 dictionary、set 或 frozen set 等)。
python
len('abc') # 3
len(['a', 'b', 'c']) # 3
len([]) # 0
len('') # 0
len(range(3)) # 3
当您定义一个类时,您可以定义的特殊方法之一是__len__()
,这些特殊方法被称为 dunder 方法,因为它们在方法名称的开头和结尾都有双下划线。Python 的内置 len( )函数调用其参数的 __len__()
方法。因此,可以为对象指定自定义长度(如果需要)。
python
class Session:
def __init__(self, number = 0):
self.number = number
def __len__(self):
return self.number
# default length is 0
s1 = Session()
print(len(s1))
# 0
# giving custom length
s2 = Session(6)
print(len(s2))
# 6
type()
传入一个对象的字面量或者标识符,将返回此对象的类型.
它有两种使用形式:
python
class type(object)
class type(name, bases, dict, **kwds)
第一种形式:
第一种形式传入一个参数时,返回 object 的类型。 返回值是一个 type 对象,通常与 object.__class__
所返回的对象相同。
推荐使用 isinstance() 内置函数来检测对象的类型,因为它会考虑子类的情况。
第二种形式:
传入三个参数时,返回一个新的 type 对象。 用来创建类。
这在本质上是 class 语句的一种动态形式:
- name 字符串即类名并会成为
__name__
属性; - bases 元组包含基类并会成为
__bases__
属性;如果为空则会添加所有类的终极基类 object。 - dict 字典包含类主体的属性和方法定义;它在成为
__dict__
属性之前可能会被拷贝或包装。
下面两条语句会创建相同的 type 对象:
python
class X:
a = 1
# 同上操作
X = type('X', (), dict(a=1))
类型对象表示各种对象类型。 对象的类型可通过内置函数 type() 来获取。 类型没有特殊的操作。 标准库模块 types 定义了所有标准内置类型的名称。类型以这样的写法来表示: <class 'int'>。
提供给三参数形式的关键字参数会被传递给适当的元类机制 (通常为 init_subclass()),相当于类定义中关键字 (除了 metaclass) 的行为方式。
可以利用 type() 传入三个参数的形式快速定义一个类:
python
o1 = type('X', (object,), dict(a='Foo', b=12))
print(type(o1))
print(vars(o1))
'''
class test:
a = 'Foo'
b = 12
'''
o2 = type('Y', (test,), dict(a='Foo', b=12))
print(type(o2))
print(vars(o2))
'''
<class 'type'>
{'a': 'Foo', 'b': 12, '__module__': '__main__', '__dict__': <attribute '__dict__' of 'X' objects>, '__weakref__': <attribute '__weakref__' of 'X' objects>, '__doc__': None}
<class 'type'>
{'a': 'Foo', 'b': 12, '__module__': '__main__', '__doc__': None}
'''
vars()
vars() 返回对象的属性和属性值的字典对象。如果没有参数,就返回当前调用位置的属性和属性值,行为类似 locals()。
语法为 vars([object])
,其中对象可有可没有。来用返回模块、类、实例或任何其它具有 __dict__
属性的对象的 __dict__
属性。
带参数的vars():
模块和实例这样的对象具有可更新的 __dict__
属性;但是,其它对象的 __dict__
属性可能会设为限制写入(例如,类会使用 types.MappingProxyType 来防止直接更新字典)。
如果指定了一个对象但它没有 __dict__
属性(例如,当它所属的类定义了 __slots__
属性时)则会引发 TypeError 异常。
不带参数的vars():
不带参数时,vars() 的行为类似 locals() ,locals() 用来更新并返回表示当前本地符号表的字典。 请注意,locals 字典仅对于读取起作用,因为对 locals 字典的更新会被忽略。
如,我们进行赋值操作时,如 bar = 1,执行赋值语句后,名称 bar 引用到值 1,名字为键,将我们要取这个值时,可以用 vars() 返回的字典里用此键取得其引用值
python
# 在终端输入
vars()
# 输出(已美化格式)
{'__name__': '__main__',
'__doc__': None,
'__package__': None,
'__loader__': <class '_frozen_importlib.BuiltinImporter'>,
'__spec__': None,
'__annotations__': {},
'__builtins__': <module 'builtins' (built-in)>
}
# 1 对象没有__dict__ 属性
vars(1)
# TypeError: vars() argument must have __dict__ attribute
class Foo:
def __init__(self):
self.__dict__ = {'name':'Foo'}
f = Foo()
vars(f)
# {'name': 'Foo'}
# 再终端执行
vars()
# 输出(已美化格式)
{'__name__': '__main__',
'__doc__': None,
'__package__': None,
'__loader__': <class '_frozen_importlib.BuiltinImporter'>,
'__spec__': None,
'__annotations__': {},
'__builtins__': <module 'builtins' (built-in)>,
'Foo': <class '__main__.Foo'>,
'f': <__main__.Foo object at 0x7fccf35f5120>
}
# 注意最后两项
isinstance()
内置函数 isinstance() 用来判断一个对象是否是一个已知的类型,或者说检测对象是不是这个类型的实例、这个类的实例。如果要判断两个类型是否相同推荐使用 isinstance()。
语法为:
python
isinstance(obj, class_or_tuple, /)
其中:
- object:要检测判断的对象
- class_or_tuple:被检测的类,支持:
- class
- type
- class 组成的元组
- types 模块中定义的 type
- union 类型
返回:
- 布尔值:返回对象是类的实例还是其子类的实例。
功能说明:
- 如果 object 参数是 class_or_tuple 参数的实例,或其(直接、间接或抽象基类)子类的实例,则返回 True。
- 如果 object 不是给定类型的对象,则总是返回 False。
- 如果 class_or_tuple 是类型对象的元组(或由该类元组递归生成)或多个类型的 union 类型,那么当 object 是其中任一类型的实例时就会返回 True。
- 如果 class_or_tuple 不是某个类型或类型元组,将会触发 TypeError 异常。
- 注:在 3.10 版本开始,class_or_tuple 可以是一个 union 类型。
如果 class_or_tuple 是元组那么以下相同:
python
isinstance(x, (A, B, ...))
# 同上
isinstance(x, A) or isinstance(x, B) or ...
python
#内置类型
a = 2
isinstance(a,int)
# True
isinstance(a,str)
# False
isinstance(a,(str,int,list)) # 是否其中一个
isinstance(a, str | int | list)
# True
#types 模块
import types
isinstance(None, types.NoneType)
# True
isinstance(print, types.BuiltinFunctionType)
# True
#自定义类
class Foo:
a = 5
foo = Foo()
isinstance(foo, Foo) # True
isinstance(foo, (list, tuple)) # False
isinstance(foo, (list, tuple, Foo)) # True
和type()的区别
区别总结为:
- type() 不会认为子类是一种父类类型,不考虑继承关系
- isinstance() 会认为子类是一种父类类型,考虑继承关系
关于 isinstance() 与 type() 的区别可以看以下示例:
python
class A:
pass
class B(A):
pass
isinstance(A(), A) # True
type(A()) == A # True
isinstance(B(), A) # True
type(B()) == A # False
有人也用 type(variable) == str
这种方式判断某个对象的类型,虽然此方法是可行的,但不提倡。
issubclass()
内置函数 issubclass()
主要用于判断参数 class 是否是类型参数 classinfo 的子类。
语法:
python
issubclass(cls, class_or_tuple, /)
参数说明:
- cls:被检测的子类
- class_or_tuple:是否 cls 的父类,形式可以是 class、type 或者它们组成的元组 tuple,元组的话只有一个满足即可
如果 cls 是 class_or_tuple 的子类(直接、间接或抽象基类),则返回 True。类被视为自身的子类。class_or_tuple 可以是类对象的元组(或递归地,其他此类元组)或联合类型, 在这种情况下,如果类是 class_or_tuple 中任何条目的子类,则返回True。在任何其他情况下,都会引发 TypeError 异常。
issubclass() 返回:
- True 如果类是类的子类,或元组的任何元素
- False 都不是
在 3.10 版更改: class_or_tuple 可以是一个 union 类型(联合类型,可以用 | 符号连接)。
python
class Polygon:
def __init__(polygonType):
print('Polygon is a ', polygonType)
class Triangle(Polygon):
def __init__(self):
Polygon.__init__('triangle')
issubclass(Triangle, Polygon)
# True
issubclass(Triangle, list)
# False
issubclass(Triangle, (list, Polygon))
# True
issubclass(Polygon, (list, Polygon))
# True
需要注意的是,类被视为自身的一个子类。
super()
super() 用于调用父类(超类)的一个方法,用来解决多重继承问题的。不仅仅可以调用父类的构造函数,还可以调用父类的成员函数。
语法如下:
python
class super(self, /, *args, **kwargs)
# or
super(type[, object-or-type])
调用形式可以有以下几种:
python
super() # 同 super(__class__, <first argument>)
super(type) # 未绑定超类对象
super(type, obj) # 绑定超类对象; 实现 isinstance(obj, type)
super(type, type2) # 绑定超类对象; 实现 issubclass(type2, type)
python
class Person(object):
def eat(self, times):
print(f'我每天吃{times}餐。')
class Student(Person):
def eat(self):
# 调用超类
super().eat(4)
tom = Student()
tom.eat()
# 我每天吃4餐。
Student.mro()
# [__main__.Student, __main__.Person, object]
调用超类方法的典型用法:
python
class C(B):
def meth(self, arg):
super().meth(arg)
也适用于类方法:
python
class C(B):
@classmethod
def cmeth(cls, arg):
super().cmeth(arg)
super() 返回一个代理对象,它会将方法调用委托给 type 的父类或兄弟类。 这对于访问已在类中被重载的继承方法很有用。
object-or-type 确定用于搜索的 MRO (method resolution order)。 搜索会从 type 之后的类开始。
举例来说,如果 object-or-type 的 __mro__
为 D -> B -> C -> A -> object 并且 type 的值为 B,则 super() 将会搜索 C -> A -> object。
object-or-type 的 __mro__
属性列出了 getattr() 和 super() 所共同使用的方法解析搜索顺序。 该属性是动态的,可以在任何继承层级结构发生更新的时候被改变。
如果省略第二个参数,则返回的超类对象是未绑定的。 如果第二个参数为一个对象,则 isinstance(obj, type)
必须为真值。 如果第二个参数为一个类型,则 issubclass(type2, type) 必须为真值(这适用于类方法)。
super 有两个典型用例。 在具有单继承的类层级结构中,super 可用来引用父类而不必显式地指定它们的名称,从而令代码更易维护。 这种用法与其他编程语言中 super 的用法非常相似。
第二个用例是在动态执行环境中支持协作多重继承。 此用例为 Python 所独有而不存在于静态编码语言或仅支持单继承的语言当中。 这使用实现"菱形图"成为可能,即有多个基类实现相同的方法。 好的设计强制要求这样的方法在每个情况下都具有相同的调用签名(因为调用顺序是在运行时确定的,也因为这个顺序要适应类层级结构的更改,还因为这个顺序可能包括在运行时之前未知的兄弟类)。
对于以上两个用例,典型的超类调用看起来是这样的:
python
class C(B):
def method(self, arg):
super().method(arg) # This does the same thing as:
# super(C, self).method(arg)
除了方法查找之外,super() 也可用于属性查找。 一个可能的应用场合是在上级或同级类中调用 描述器。
请注意 super() 是作为显式加点属性查找的绑定过程的一部分来实现的,例如 super().__getitem__(name)
。 它做到这一点是通过实现自己的 __getattribute__()
方法,这样就能以可预测的顺序搜索类,并且支持协作多重继承。 对应地,super() 在像 super()[name] 这样使用语句或操作符进行隐式查找时则未被定义。
还要注意的是,除了零个参数的形式以外,super() 并不限于在方法内部使用。 两个参数的形式明确指定参数并进行相应的引用。 零个参数的形式仅适用于类定义内部,因为编译器需要填入必要的细节以正确地检索到被定义的类,还需要让普通方法访问当前实例。
super() 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。MRO 就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表。
在类的继承中,如果重定义某个方法,该方法会覆盖父类的同名方法,但有时,我们希望能同时实现父类的功能,这时,我们就需要调用父类的方法了。
事实上,对于你定义的每一个类,Python 会计算出一个方法解析顺序(Method Resolution Order, MRO)列表,它代表了类继承的顺序,我们可以使用下面的方式获得某个类的 MRO 列表,以页案例为例:
python
Student.__mro__
Student().__class__.mro()
Student.mro()
# [__main__.Student, __main__.Person, object]
这个列表真实的列出了类C的继承顺序。Student->Person->object。在方法调用时,是按照这个顺序查找的。
那这个 MRO 列表的顺序是怎么定的呢,它是通过一个 C3 线性化算法来实现的,这里我们就不去深究这个算法了,一个类的 MRO 列表就是合并所有父类的 MRO 列表,并遵循以下三条原则:
- 子类永远在父类前面
- 如果有多个父类,会根据它们在列表中的顺序被检查
- 如果对下一个类存在两个合法的选择,选择第一个父类
super 的工作原理如下:
python
def super(cls, inst):
mro = inst.__class__.mro()
return mro[mro.index(cls) + 1]
其中,cls 代表类,inst 代表实例,上面的代码做了两件事:
- 获取 inst 的 MRO 列表
- 查找 cls 在当前 MRO 列表中的 index, 并返回它的下一个类,即 mro[index + 1]
当你使用 super(cls, inst) 时,Python 会在 inst 的 MRO 列表上搜索 cls 的下一个类,因此 super() 和父类没有实质性的关联。
比如在下列的菱形继承关系中,C 的 MRO 列表是 C->A->B->Base->object:
python
Base
/ \
/ \
A B
\ /
\ /
C
代码如:
python
class Base(object):
def __init__(self):
print("enter Base")
print("leave Base")
class A(Base):
def __init__(self):
print("enter A")
super(A, self).__init__()
print("leave A")
class B(Base):
def __init__(self):
print("enter B")
super(B, self).__init__()
print("leave B")
class C(A, B):
def __init__(self):
print("enter C")
super(C, self).__init__()
print("leave C")
c = C()
'''
enter C
enter A
enter B
enter Base
leave Base
leave B
leave A
leave C
'''
C.mro()
# [__main__.C, __main__.A, __main__.B, __main__.Base, object]
enter A 的下一句不是 enter Base 而是 enter B,原因是,super 和父类没有实质性的关联,是按照 mro 顺序运行的。
因此,super() 的主要作用:
- 允许我们避免显式使用基类名称
- 父类的名称是不固定的(避免改名、改变继承方式),省去修改代码
- 传入的 arg 本身也是动态的,比较灵活
- 使用多重继承
object()
object() 函数返回一个空对象,不能向该对象添加新属性或方法。这个对象是所有类的基础,它拥有所有类默认的内置属性和方法。
python
o = object()
哨兵值(Sentinel value)是一个特殊值(唯一),因为和所有其它值不同,所以可以作为区分其它值得标记。
可以理解为它是一个唯一的占位符值,在 Python 程序中对一些事情很有用,例如函数参数的默认值,其中没有一个是有效的输入值。
object() 的实例,在其它任何地方都不会出现,不会出现重复的可能:
python
>>> x = object()
>>> o = object()
>>> o is x
False
>>> o == x
False
因些很多场景下用它做缺失值(MISSING)的替代。我们看这样的一个例子:
python
sentinel = object() # 用于指示缓存未命中的唯一对象
result = cache_get(key, sentinel)
if result is not sentinel:
...
print()
print() 是 python 最美的语句,它会将你知道的内容,不知道(在变量里)的内容打印输出。
语法为:
python
print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)
它将值打印到流或 sys.stdout(默认)。其中 objects 是一个序列对象,语法表示中可见它可以被解包,其他可选的关键字参数有:
- file: 类似文件的对象(流);默认为当前系统的 sys.stdout
- sep: 在输出的值之间插入(分隔)字符串,默认为空格
- end: 最后一个值后追加的字符串,默认为换行符
- flush: 是否强制刷新
以上四个参数必须以关键字参数的形式给出,所有非关键字参数都会被转换为字符串,就像是执行了 str() 一样,并会被写入到流,以 sep 且在末尾加上 end。 sep 和 end 都必须为字符串;它们也可以为 None,这意味着使用默认值。 如果没有给出 objects,则 print() 将只写入 end。
file 参数必须是一个具有 write(string) 方法的对象;如果参数不存在或为 None,则将使用 sys.stdout。 由于要打印的参数会被转换为文本字符串,因此 print() 不能用于二进制模式的文件对象。 对于这些对象,应改用 file.write(...)
。输出是否缓存通常取决于 file,但如果 flush 关键字(在 3.3 版新增)参数为 True,输出流会被强制刷新。
python
pi = 3.14
print(pi)
# 3.14
#输出多个内容
print("圆周率", pi)
# 圆周率 3.14
#中间用空格分隔,变量也帮我们打印出了具体的值。试试看以下代码:
print(8848, '新的高度。')
print(1, 2, 3, 4)
print('盖若', '是个好网站')
print('---------')
#连接
print("圆周率", pi, sep=":")
# 圆周率:3.14
#两个值之间多了个: 分隔。试试以下代码:
print(1, 2, 3, 4, sep="-")
# 1-2-3-4
print(1, 2, 3, 4, sep="\n") # \n 代表换行
'''
1
2
3
4
'''
print(1, 2, 3, 4, sep="\t") # \t 代表一个制表符号,就是 tab
# 1 2 3 4
#结尾连接
print(1, end=" " )
print(2, end=" " )
# 1 2
flush 参数示例
如同 C 语言,print() 使用标准输出。这不过是操作系统中的"文件",程序的文本输出在操作系统中发送,因此可以显示给用户。
默认情况下,stdout 是缓冲的。也就是说,在收到特殊换行代码 \n
之前,它会存储接收到的数据而不显示数据。Python 的 print 函数会自动增加换行符给发送给它的任何字符串。但有时这种行为是不可取的,你想在同一行上显示多个东西。
在下例中,将使用 print 函数的 end 参数以换行符以外的字符(或空字符串)结束字符串,这样您就不会打印除所要求的内容之外的任何内容。以下代码在同一行上打印几个点,每 0.5s 打印一个点(注意:在终端中执行,不要在 Notebook):
python
# 参考:https://itqna.net/questions/37569/what-print-flush-python-closed
import time
for _ in range(5):
print('.', end='')
time.sleep(0.5)
print(' Gairuo!')
# 在终端中最后一次性显示
# ..... Gairuo!
但在大多数情况下,2.5 秒内什么也没发生,然后所有的点都同时出现。这是因为在默认情况下,stdout 在接收到新行之前不会显示任何内容,而我们直到最后才会发送新行。那么我们如何解决这个问题呢?很简单,只需使用 flush 强制立即显示结果,即使没有新行:
python
import time
for _ in range(5):
print('.', end='', flush=True)
time.sleep(0.5)
print(' Gairuo!')
# 依次显示以下内容
# ..... Gairuo!
值得一提的是,flush 作为 print 的增强参数,仅在Python 3.3 中提供。在此之前,它必须手动调用:
python
import sys
import time
for _ in range(5):
print('.', end='')
sys.stdout.flush() # 这儿
time.sleep(0.5)
print(' Gairuo!')
这个 flush 参数可以帮我们做出类似执行进度条之类的功能。
使用场景:
- 尤其是在 while 循环中,要想每进行一次 while 循环体,在屏幕上更新打印的内容就得使用 flush=True 的参数。
- 打开一个文件, 向其写入字符串, 在关闭文件 f.close() 之前, 打开文件可能是看不到写入的字符的, 要想在关闭之前实时的看到写入的字符串,应该用 flush=True
input()
内置函数 input() 函数接受一个标准输入数据,返回为 string 类型。它用于从控制台读取用户输入的内容,一般在交互模式下,让用户通过输入来控制程序。
它的语法是 input([prompt])
,可选参数 prompt 起到提示作用,可以告诉用户如何输入,输入什么格式。
如果存在 prompt 实参,则将其写入标准输出,末尾不带换行符。接下来,该函数从输入中读取一行,将其转换为字符串(除了末尾的换行符)并返回。当读取到 EOF 时,则触发 EOFError。
如果加载了 readline 模块,input() 将使用它来提供复杂的行编辑和历史记录功能。
引发一个 审计事件 builtins.input 附带参数 prompt。
在成功读取输入之后引发一个审计事件 builtins.input/result 附带结果。
python
>>> s = input('--> ')
# -->
# 此时提示用户输入,我们输入:Monty Python's Flying Circus
>>> s
"Monty Python's Flying Circus"
age = int(input('你哪年出生?'))
print(f'你的年龄为{2022-age}')
# 你哪年出生?
# 输入 2000,回车
# 输出:
# 你的年龄为22
range()
range对象
range 对象是(rangeobject) Python 内置的对象,它可以认为是一个不可变的数字序列,通常用于在 for 循环中指定循环的次数。虽然被称为函数,但 range 实际上是一个不可变的序列类型。
语法:
range(stop)
range(start, stop[, step])
参数:
- start: 可选,数列的开始,默认从 0
- stop: 必传,数列的结束(不包含)
- step: 可选,步长,默认是 1
参数说明:
- 以上三个参数必须是整数(integers),或者内置 int 类型,以及任何实现
__index__()
特殊方法的对象 - 如果省略 step 参数,则默认为 1
- 如果省略 start 参数,则默认为 0
- 如果步长为零,则会引发 ValueError
生成规则
- 如果 step 为正值,确定 range r 内容的公式为
r[i] = start + step*i
其中i >= 0
且r[i] < stop
。 - 如果 step 为负值,确定 range 内容的公式仍然为
r[i] = start + step*i
,但限制条件改为i >= 0
且r[i] > stop
。 - 如果 r[0] 不符合值的限制条件,则该 range 对象为空。 range 对象确实支持负索引,但是会将其解读为从正索引所确定的序列的末尾开始索引。
- 元素绝对值大于
sys.maxsize
的 range 对象是被允许的,但某些特性 (例如 len()) 可能引发 OverflowError。
特点:
相比常规 list 或 tuple,range 对象总是占用固定数量的(较小)内存,不论其所表示的范围有多大,因为它只保存了 start、stop 和 step 值,并会根据需要计算具体单项或子范围的值。
属性和方法
range() 函数创建的 rangeobject,有三个属性:
python
# 定义范围对象 rangeobject
r = range(1, 10, 2)
list(r) # 转为列表
# [1, 3, 5, 7, 9]
# 属性
r.start # 1 开始值
r.stop # 10 终止值
r.step # 2 步长
# 返回值的出现次数
r.count(1) # 1
r.count(10) # 0
# 返回值的索引
r.index(3) # 1
# 如果该值不存在,则引发值错误
r.index(10) # ValueError: 10 is not in range
使用
可用列表推导式来展开 range 对象:
[i for i in range(4)] # [0, 1, 2, 3]
用在 for 循环中:
for i in range(2, 10, 2):
print(i, end=' ')
# 4 6 8
如果,不知道要生成多长的 range, 可以通过 len()
来获取长度:
var = 'hello'
for i in range(len(var)):
print(var[i])
也可以使用len函数来获取range可以获得的列表的长度
python
r = range(1, 10)
print(list(r))
print(list(r))
print(len(r))
r = range(1, 15)
print(len(r))
'''
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
9
14
'''
range 对象是序列(Sequence)
range对象实现了 collections.abc.Sequence
抽象基类(ABC),提供如包含检测、元素索引查找、切片等特性,并支持负索引。
python
from collections import abc
isinstance(r, abc.Sequence)
# True
type(r)
# range
issubclass(range, abc.Sequence)
# True
range 对象实现了 一般 序列的所有操作,但拼接和重复除外(这是由于 range 对象只能表示符合严格模式的序列,而重复和拼接通常都会违反这样的模式)。
python
r = range(0, 20, 2)
r
# range(0, 20, 2)
list(r)
# [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
len(r)
# 10
max(r)
# 18
sum(r)
# 90
iter(r)
# <range_iterator at 0x7f9b3a7ad4a0>
11 in r
# False
10 in r
# True
r.index(10)
# 5
r[5]
# 10
r[:5]
# range(0, 10, 2)
r[-1]
# 18
不支持拼接和重复:
python
r + [1,2]
# TypeError: unsupported operand type(s) for +: 'range' and 'list'
r + r
# TypeError: unsupported operand type(s) for +: 'range' and 'range'
r*3
# TypeError: unsupported operand type(s) for *: 'range' and 'int'
使用 == 和 != 检测 range 对象是否相等是将其作为序列来比较。 也就是说,如果两个 range 对象表示相同的值序列就认为它们是相等的。 (请注意比较结果相等的两个 range 对象可能会具有不同的 start, stop 和 step 属性,例如 range(0) == range(2, 1, 3) 而 range(0, 3, 2) == range(0, 4, 2)。)
all()
all() 的作用是,如果传入的可迭代对象的所有元素均为真值(或可迭代对象为空)则返回 True。它与any()有着不同的逻辑。
语法为:
all(iterable)
- iterable(可迭代对象):可以是任意的可迭代对象 (list, tuple, dictionary, 等),包含一定数量的元素。
返回:
all() 函数中如果给定 iterable (可迭代对象)中的所有元素:
- True - 如果 iterable 中的所有元素都为 true
- False - 如果 iterable 中的任何元素为 false(至少有一个为 False)
注:元素除了是 0、空、None、False 外都算 True,空元组、空列表返回值为True,要特别注意。
如果我们检测变量,以下情况会得到假,其他情况为真。
- None, False
- 数值中的
0
,0.0
,0j
(虚数),Decimal(0)
,Fraction(0, 1)
- 空字符串
""
、空元组()
、空列表[]
- 空字典
{}
、空集合set()
- 类的对象特殊方法
__bool__()
返回 False 或是定义了__len__()
方法且返回 0
all()函数等价于:
python
def all(iterable):
for element in iterable:
if not element:
return False
return True
以下是 all() 返回值参照表:
情况 | 返回值 |
---|---|
所有值为 true | True |
所有值为 false | False |
一个值为 true (其他为 false) | False |
一个值为 false (其他为 true) | False |
空的可迭代对象 | True |
字符串(包括空串) | True |
字典所有键为 true | True |
字典一个键为 false | False |
注:对于字典,如果所有键(不是值)都为 true 或字典为空,则 all() 返回 true。否则,对于所有其他情况,它将返回 false。
以下是一些案例:
python
# 所有值为 true
iterable = [1, 2, 3, 4]
all(iterable) # True
# 所有值为 false
iterable = [0, False]
all(iterable) # False
# 一个为 true
iterable = [0, False, 1]
all(iterable) # False
# 一个为 false
iterable = [1, 3, 4, 0]
all(iterable) # False
# 空对象
iterable = ()
all(iterable) # True
# 字符串
iterable = 'Hello'
all(iterable) # True
# 字典
d = {0: 'False', 1: 'False'}
all(d) # False
all([False, None, 0, 0.0, 0 + 0j, '', [], {}, ()])
# True
not all([False, False, False])
# False
not all([True, False, False])
# True
# 列表推导式
l = [0, 1, 2, 3, 4]
all([i > 2 for i in l])
# False
any([i > 2 for i in l])
# True
# 可以直接在生成器上使用,比列表快很多
(i > 2 for i in l)
# <generator object <genexpr> at 0x7fe33e7e3dd0>
all(i > 2 for i in l)
# False
any(i > 2 for i in l)
# True
any()
any() 的作用是,如果传入的可迭代对象的至少一个元素为真值则返回 True
语法形式为:
any(iterable)
函数接受单个参数:
- iterable(可迭代对象):可以是任意的可迭代对象 (list, tuple, string, dictionary, 等),包含一定数量的元素。
返回:
any() 函数中如果给定 iterable (可迭代对象)中的所有元素:
- True - 如果 iterable 中至少有一个元素为 true
- False - 如果所有元素都为 false 或 iterable 为空
注:元素除了是 0、空、None、False 外都算 True,空元组、空列表返回值为True,要特别注意。
如果我们检测变量,以下情况会得到假,其他情况为真。
- None, False
- 数值中的
0
,0.0
,0j
(虚数),Decimal(0)
,Fraction(0, 1)
- 空字符串
""
、空元组()
、空列表[]
- 空字典
{}
、空集合set()
- 类的对象特殊方法
__bool__()
返回 False 或是定义了__len__()
方法且返回 0
any()函数等价于:
python
def any(iterable):
for element in iterable:
if element:
return True
return False
any()函数返回值参照表:
情况 | 返回值 |
---|---|
所有值为 true | True |
所有值为 false | False |
一个值为 true (其他为 false) | True |
一个值为 false (其他为 true) | True |
空的可迭代对象 | False |
字符串(不包括空串) | True |
空字符串 | False |
字典所有键为 true | True |
字典一个键为 false | False |
注:对于字典,如果所有键(不是值)都为 false 或字典为空,any() 将返回 false。如果至少有一个键为 true,any() 将返回 true。
以下是一些案例:
python
# True since 1,3 and 4 (at least one) is true
l = [1, 3, 4, 0]
any(l) # True
# False since both are False
l = [0, False]
any(l) # False
# True since 5 is true
l = [0, False, 5]
any(l) # True
# False since iterable is empty
l = []
any(l) # False
# Atleast one (in fact all) elements are True
s = "This is good"
any(l) # True
# 0 is False
# '0' is True since its a string character
s = '000'
any(l) # True
# False since empty iterable
s = ''
any(l) # False
# 0 is False
d = {0: 'False'}
any(l) # False
# 1 is True
d = {0: 'False', 1: 'True'}
any(l) # True
# 0 and False are false
d = {0: 'False', False: 0}
any(l) # False
# iterable is empty
d = {}
any(l) # False
# 0 is False
# '0' is True
d = {'0': 'False'}
any(l) # True
any([False, None, 0, 0.0, 0 + 0j, '', [], {}, ()])
# False
not any([False, False, False])
# True
not any([True, False, False])
# False
# 列表推导式
l = [0, 1, 2, 3, 4]
all([i > 2 for i in l])
# False
any([i > 2 for i in l])
# True
# 可以直接在生成器上使用,比列表快很多
(i > 2 for i in l)
# <generator object <genexpr> at 0x7fe33e7e3dd0>
all(i > 2 for i in l)
# False
any(i > 2 for i in l)
# True
max()和min()
内置函数 max() 可传入一个可迭代对象 或者多个元素作为参数,返回这个它们中的最大元素。同样,min() 可得到最小值。内置函数 min() 与 max() 的逻辑相同,区别仅为返回最小的项,这里仅以max()为例说明。
语法:
python
max(iterable, *[, default=obj, key=func]) -> value
max(arg1, arg2, *args, *[, key=func]) -> value
非数字类型如果判断大小,可以参考 sorted() 中的排序原理。
第一种,可迭代对象:
如果只提供了一个位置参数,它必须是非空 iterable,返回可迭代对象中最大的元素;使用单个 iterable 参数,返回其最大项。如果提供的 iterable 为空,则默认的仅关键字参数指定要返回的对象。其中:
- iterable:可迭代对象,如列表、元素、字典等等
*iterables
(可选):任意数量的序列,如果我们传递多个序列,则返回给定所有序列中最大的项- key (可选):传递 iterables 并根据其返回值执行比较的关键函数
- default (可选):如果给定的 iterable 为空,则为此默认值,不传此值则不允许 iterable 为空对象
第二种,多值:
如果提供了两个(最少)及以上的位置参数,则返回最大的位置参数。
python
max(arg1, arg2, *args, *[, key=func]) -> value
这种形式的参数为:
- arg1:要对比的对象,可以是数字、字符串等
- arg2:要对比的对象,可以是数字、字符串等
*args
(可选) :任意数量的对象,注意,前边两个参数是必传的- key (可选) :传递每个参数并根据其返回值执行比较的键函数
max() 函数会在两个或多个对象之间查找最大的项。
key 实参指定排序函数用的参数,如传给 list.sort() 的。default 实参是当可迭代对象为空时返回的值。如果可迭代对象为空,并且没有给 default ,则会触发 ValueError。
如果有多个最大元素,则此函数将返回第一个找到的。这和其他稳定排序工具如 sorted(iterable, key=keyfunc, reverse=True)[0] 和 heapq.nlargest(1, iterable, key=keyfunc) 保持一致。
可参考 sorted() 中关于 key 参数的介绍。
python
max(1,2,3) # 3
max('123') # '3'
max('1, 2, 3') # '3'
max('abc') # 'c'
d = {'a': 3, 'b': 2, 'c': 1}
max(d) # 'c'
max(d.items()) # ('c', 1)
max(d, key=lambda k: d[k]) # 'a'
max([22, 2], [42, 1]) # [42, 1]
max([(1,5),(2,4),(3,3)]) # (3, 3)
max([(1,5),(2,4),(3,3)], key=lambda x: x[1])
# (1, 5)
max([*range(2)], [*range(7)])
# [0, 1, 2, 3, 4, 5, 6]
max(0, True, -1) # True, True 为 1
max(range(10)) # 9
max([], default=10) # 10
python
class P():
def __init__(self, name, age) -> None:
self.name = name
self.age = age
def __lt__(self, other):
if self.age < other.age:
return True
def __str__(self) -> str:
return f'<{self.name}, {self.age}>'
a = P('tom', 18)
b = P('lily', 19)
print(max(a, b)) # <lily, 19>
print(min(a, b)) # <tom, 18>
sum()
将一个序列的所有值进行求和并返回,同时如果需要从一个固定值起始计算,可以指定 start 参数。
语法是:
sum(iterable, /, start=0)
参数有:
- iterable:可迭代对象,如:列表、元组、集合,将会对这些数据进行相加或者连接
- start:起始值,保底值,从此值开始再加上序列中的所有值,默认为 0。
此函数专门用于数值,可能会拒绝非数值类型。
内置函数 sum 相当于:
python
def sum(values, start=0):
total = start
for value in values:
total = total + value
return total
zip()
内置函数 zip() 上用来创建一个聚合了来自每个可迭代对象中的元素的迭代器。它获取 iterables(可以是零或更多),将它们聚合成多个元组形成的可迭代对象,然后返回。
语法为:
zip(*iterables)
参数 iterables 可以是一个内置的可迭代对象 (如: list, string, dict), 或者是用户自己定义的可迭代对象。简单说,就是可以传入一个或者多个(一般是两个)类似列表或者元组那样的可迭代对象,如 zip([1,2], [3,4])
等。同时,它还支持参数为空。
返回一个基于参数 iterable 对象的元组迭代器(zip 对象)。根据传入参数的情况,它返回的内容分别为:
- 不传递任何参数,将返回一个空迭代器(zip 对象)
- 传递一个可迭代对象,返回一个元组迭代器,每个元组只有一个元素
- 传递多个可迭代对象,返回一个元组迭代器,每个元组都包含来自所有 iterables 的元素, 迭代器在最短 iterable 耗尽时停止。
针对第三种情况,如一个包含 2 个元素,另一个包含 3 个元素,则返回的迭代器将包含 2 个元组,每个元组以第一个元组的 2 个元素开头,传入的第二个元组的最后一个数据被丢弃。
python
# 无传入
z = zip()
z # <zip at 0x7fe33d1ac440>
list(z)
# []
# 传入一个
z = zip([1,2,3])
list(z)
# [(1,), (2,), (3,)]
# 传入两个
z = zip([1,2,3],[3,4,5])
list(z)
# [(1, 3), (2, 4), (3, 5)]
# 不同长度
z = zip([1,2],[3,4,5])
list(z)
# [(1, 3), (2, 4)]
z = zip([1,2,3],[4,5])
list(z)
# [(1, 4), (2, 5)]
# 解包
coordinate = ['x', 'y', 'z']
value = [3, 4, 5]
result = zip(coordinate, value)
result_list = list(result)
result_list
# [('x', 3), ('y', 4), ('z', 5)]
c, v = zip(*result_list)
c # ('x', 'y', 'z')
v # (3, 4, 5)
在日常应用中,我们一般按照一定的顺序将不同事物的数据存起来,比如我们将 lily、lucy、amy 的身高、体重、年龄按顺序存起来,形成三个列表:
python
# 分别是每个人的 身高、体重、年龄
lily = [168, 50, 22]
lucy = [170, 55, 25]
amy = [175, 53, 24]
由于每个列表是单一女生的数据,我们求平均身高时可以先操作 zip:
python
z = zip(lily,lucy,amy)
list(z)
# [(168, 170, 175), (50, 55, 53), (22, 25, 24)]
此时,返回的列表中的第一个值就是所有的身高数据,就可以方便地计算出结果:
python
z = zip(lily,lucy,amy)
h = list(z)[0]
sum(h)/3
# 171.0
其他案例:
python
names = ['Alice', 'Bob', 'Charlie']
ages = [24, 50, 18]
for name, age in zip(names, ages):
print(name, age)
# Alice 24
# Bob 50
# Charlie 18
另外,zip 对象也可使我们方便地迭代组合信息。
我们知道,如果传入的多个可迭代对的长度不相等,则取最短的进行对应,这类似于木桶效应的短板现象,如果想按最长的处理,不足的用 None 补足补齐,可以使用 itertools 中的 zip_longest 方法:
python
import itertools
z = itertools.zip_longest([1,2],[3,4,5])
list(z)
# [(1, 3), (2, 4), (None, 5)]
str()
str() 返回对象的字符形式,它是 str 版本的 object。str 是内置的字符串类,一般用它来将对象转为字符类型。
str() 的语法是:
python
class str(object='')
class str(object=b'', encoding='utf-8', errors='strict')
参数说明:
- object - 要返回其字符串表示形式的对象,如果未提供,则返回空字符串
- encoding - 给定对象的编码,默认值 UTF-8
- errors - 解码失败时的响应。默认为 'strict' 严格的,可选择以下 6 种:
- strict - 在失败时引发 UnicodeDecodeError 异常的默认响应
- ignore - 忽略结果中不可编码的 Unicode
- replace - 将不可编码的 Unicode 替换为问号
- xmlcharrefreplace - 插入 XML 字符引用而不是不可编码的 Unicode
- backslashreplace - 插入 \uNNNN 空间序列而不是不可编码的 Unicode
- namereplace - 插入 \N{...} 转义序列而不是不可编码的 Unicode
返回:
返回一个字符串,该字符串被认为是给定对象的非正式或可打印的表示形式。
如果 encoding 或 errors 均未给出,str(object)
返回 object.__str__()
,这是 object 的"非正式"或格式良好的字符串表示。 对于字符串对象,这是该字符串本身。 如果 object 没有 __str__()
方法,则 str() 将回退为返回 repr(object)
。
如果 encoding 或 errors 至少给出其中之一,则 object 应该是一个 bytes-like object (例如 bytes 或 bytearray)。 在此情况下,如果 object 是一个 bytes (或 bytearray) 对象,则 str(bytes, encoding, errors) 等价于 bytes.decode(encoding, errors)。 否则的话,会在调用 bytes.decode() 之前获取缓冲区对象下层的 bytes 对象。
将一个 bytes 对象传入 str() 而不给出 encoding 或 errors 参数的操作属于第一种情况, 将返回非正式的字符串表示
python
str(88)
# '88'
str('Gairuo!')
# 'Gairuo!'
str(b'Gairuo!')
# "b'Gairuo!'"
# bytes
b = bytes('gairuö', encoding='utf-8')
str(b, encoding='ascii', errors='ignore')
# 'gairu'
在上边 bytes 实例中,字符"ö"不能被 ASCII 解码。因此,它应该给出一个错误。但是,我们已将错误设置为"忽略"。因此,Python会忽略 str()
无法解码的字符。
自定义字符形式
通过对象类的特殊方法 object.__str__()
可以自定义返回的字符内容:
python
class Student(object):
def __init__(self, name):
self.name = name
def __str__(self):
return repr(f'我是{self.name}')
me = Student('大明')
str(me)
# "'我是大明'"
repr()
repr 是 representation(描述、表现形式)的缩写,对象的 repr 的目的是返回可以包含在表达式中的字符串,其中同一对象的 str 是可以自然显示的字符串。
python
print(repr(a))
print('{!r}'.format(a))
print('%r'%a)
上边代码的 r 是 repr 的缩写。repr 在调试代码时最有用,尤其是在编写自己的自定义代码时。
repr(object) 返回对象的可打印形式字符串。对于很多类型而言,本函数试图返回的字符串,会与将对象传给 eval() 所生成的结果相同。
它的语法形式是 repr(object)
传入的是一个任意的对象,返回的给定对象的可打印表示形式。对于许多类型,此函数尝试返回一个字符串,该字符串将在传递给 eval()
时生成具有相同值的对象,否则,表示是一个尖括号中的字符串,其中包含对象类型的名称以及其他信息,通常包括对象的名称和地址(见下例)。类对象可以通过定义特殊方法 _repr__()
方法来控制此函数为其实例返回的内容。
以下是定义了一个整型对象,我们用 repr()
返回它的打印形成(它是一个字符串),当我们将这个结果传给 eval()
时,它生成一个原来的整型对象:
python
n = 10
repr(n)
# '10'
eval(repr(n))
# 10
其他例子:
python
repr(True)
# 'True'
repr(bool)
# "<class 'bool'>"
import math
repr(math)
# "<module 'math' from '../math.cpython-310-darwin.so'>"
repr(repr)
# '<built-in function repr>'
import pandas as pd
repr(pd.Series)
# "<class 'pandas.core.series.Series'>"
import numpy as np
repr(np.array([*'abc']))
# "array(['a', 'b', 'c'], dtype='<U1')"
repr(__name__)
# "'__main__'"
repr(int|str)
# 'int | str'
# 自定义类
class student:
pass
repr(student())
# '<__main__.student object at 0x7fec2f1d2080>'
自定义__repr__
通过重载特殊方法 __repr__()
可自定义对象的 repr()
返回值。
python
class Student(object):
def __init__(self, name):
self.name = name
def __repr__(self):
return repr(f'我是{self.name}')
me = Student('大明')
repr(me)
# "'我是大明'"
repr()
返回的有得于解释器读取的形式,string 格式,因此将 string 格式可以传给 eval()
进行执行。
如果我们使用 repr()
函数打印字符串,然后它会用一对引号打印,如果我们计算一个值,我们会得到比 str()
函数更精确的值。
str()
与repr()的区别
str()
用于为最终用户创建输出,而repr()
主要用于调试和开发。repr()
的目标是明确无误,str()
的目标是可读性。例如,如果我们怀疑一个浮点数有一个小的舍入误差,repr 将显示给我们,而 str 可能没有。repr()
计算对象的"正式"的字符串表示形式(包含该对象的所有信息的表示形式),str()
用于计算对象的"非正式"(友好的)字符串表示形式(用于打印该对象的表示形式)。print()
打印语句的str()
内置特殊函数使用__str__
显示对象的字符串表示形式,页repr()
内置特殊函数使用__repr__
以显示对象。
python
import datetime
now = datetime.datetime.now()
# 打印日期时间对象的可读格式
print(now)
str(now)
# 2021-11-10 22:47:52.471301
# 打印日期时间对象的正式格式
repr(now)
# datetime.datetime(2021, 11, 10, 22, 47, 52, 471301)
eval()
eval() 将会执行执行一个以字符串为形式的 Python 表达式。 他非常强大,同时也非常危险,所以我们在使用时要格外小心。
eval() 的语法:
python
eval(source, globals=None, locals=None, /)
在全局 globals() 和局部 locals() 命名空间的上下文中计算执行给定的 source 字符串表达式。
source 可以是表示Python表达式的字符串,也可以是 compile() 返回的代码对象。
全局变量 globals 必须是字典,局部变量 locals 可以是任何映射类型,默认为当前全局变量和局部变量。
如果只提供 globals,则 locals 默认也是它。
即:
- expression :类型可以是 str、bytes、CodeType,要执行表达式或者代码。
- globals :(可选参数)变量作用域,全局命名空间,如果被提供,则必须是一个字典对象。
- locals : (可选参数)变量作用域,局部命名空间,如果被提供,可以是任何映射对象。
python
eval('1+2')
# 3
a = 2
eval('a*4')
# 8
eval('[*"123"]')
# ['1', '2', '3']
eval('{"a":1}')
# {'a': 1}
eval('{"b": c}', {'c':10})
# {'b': 10}
eval('[i for i in range(2)]')
# [0, 1]
表达式解析参数 expression 并作为 Python 表达式进行求值(从技术上说是一个条件列表),采用 globals 和 locals 字典作为全局和局部命名空间。 如果存在 globals 字典,并且不包含 __builtins__
键的值,则在解析 expression 之前会插入以该字符串为键以对内置模块 builtins 的字典的引用为值的项。 这样就可以在将 globals 传给 eval()
之前通过向其传入你自己的 __builtins__
字典来控制可供被执行代码可以使用哪些内置模块。 如果 locals 字典被省略则它默认为 globals 字典。 如果两个字典都被省略,则将使用调用 eval()
的环境中的 globals 和 locals 来执行该表达式。 注意,eval() 无法访问闭包环境中的 嵌套作用域 (非局部变量)。
返回值就是表达式的求值结果。 语法错误将作为异常被报告。
该函数还可用于执行任意代码对象(比如由 compile() 创建的对象)。 这时传入的是代码对象,而非一个字符串了。如果代码对象已用参数为 mode 的 'exec'
进行了编译,那么 eval() 的返回值将为 None。
提示: exec() 函数支持语句的动态执行。 globals() 和 locals() 函数分别返回当前的全局和本地字典,可供传给 eval() 或 exec() 使用。
如果给出的源数据是个字符串,那么其前后的空格和制表符将被剔除。
python
# 在指定命名空间中执行表达式
namespace = {'a': 2, 'b': 3}
result = eval("a + b", namespace)
print(result) # 输出: 5
#eval传递全局变量参数,注意字典里的:age中的age没有带引号,说明它是个变量,而不是字符串。
#这里两个参数都是全局的
print(eval("{'name':'linux','age':age}",{"age":1822}))
#输出结果:{'name': 'linux', 'age': 1822}
print(eval("{'name':'linux','age':age}",{"age":1822},{"age":1823}))
#输出结果:{'name': 'linux', 'age': 1823}
#eval传递本地变量,既有global和local时,变量值先从local中查找。
age=18
print(eval("{'name':'linux','age':age}",{"age":1822},locals()))
#输出结果:{'name': 'linux', 'age': 18}
print("-----------------")
print(eval("{'name':'linux','age':age}"))
使用风险
考虑使用 UNIX 系统(Mac OS,Linux等)的情况,然后导入 OS 模块。操作系统模块提供了一种可移植的方式来使用操作系统功能,如读取或写入文件。
如果允许用户使用 eval(input())
输入一个值,则用户可以使用命令:os 发出命令来更改文件,甚至删除所有文件:os.system('rm -rf *')
。警告:千万别试啊!
eval 虽然方便,但是 要注意安全性,可以将字符串转成表达式并执行,就可以利用执行系统命令,删除文件等操作。比如用户恶意输入就会获得当前目录文件。
我们可以适当限制命名空间:
python
from math import *
eval('dir()')
# 所有 math 的命名空间
eval('dir()', {})
# ['__builtins__']
eval('dir()', {'sqrt': sqrt, 'pow': pow})
# ['__builtins__', 'pow', 'sqrt']
# 禁止内置命名空间,内置函数等
eval('dir()', {'__builtins__': None})
# TypeError: 'NoneType' object is not subscriptable
注意:有时,eval() 即使名称有限也不安全。当一个对象及其方法可以访问时,几乎任何事情都可以完成。唯一安全的方法是验证用户输入。
ast.literal_eval
在 Python 中,eval() 函数可以将字符串作为代码执行。但是,使用 eval() 函数时需要非常小心,因为它可以执行任何代码,包括恶意代码。为了确保安全,可以使用 ast.literal_eval() 函数来代替 eval() 函数。ast.literal_eval() 函数只能评估一些基本的 Python 表达式,例如字符串、数字、元组、列表、字典和布尔值,因此它比 eval() 函数更安全。
ast.literal_eval(node_or_string) 计算表达式节点或仅包含Python表达式的字符串。提供的字符串或节点只能由以下Python文本结构组成:字符串、字节、数字、元组、列表、dicts、set、boolean 和 None。
compile()
compile() 函数将指定的源(普通字符串、字节串或 AST 对象)作为代码对象返回。
compile() 方法返回的代码对象,可以使用 exec() 和 eval() 等方法调用,它们将执行动态生成的 Python 代码。
语法:
python
compile(
source,
filename,
mode,
flags=0,
dont_inherit=False,
optimize=-1,
*,
_feature_version=-1,
)
参数说明:
- source - 源代码可以表示 Python 模块、语句或表达式,包括 string、byte string、AST object
- filename - 从中读取代码的文件。如果它不是从文件中读取的,你可以自己给它起个名字
- mode - 字符串,exec 或 eval 或 single。
- eval - 只接受一个表达式。
- exec - 它可以采用包含Python语句、类和函数等的代码块
- single - 如果它由单个交互语句组成
- flags (可选) - 控制未来哪些语句会影响源代码的编译。默认值:0
- dont_inherit (可选) - 如果为true,则会停止继承代码调用中任何未来有效语句效果的编译
- optimize (可选) - 编译器的优化级别。默认值为-1。
将源代码编译成可由 exec() 或 eval()执行的代码对象。
python
codeInString = 'a = 5\nb=6\nsum=a+b\nprint("sum =",sum)'
codeObejct = compile(codeInString, 'sumstring', 'exec')
exec(codeObejct)
# sum = 11
eval(codeObejct)
# sum = 11
由 compile() 生成的代码对象可以由内置函数 eval() 和exec()执行并得到执行结果。
compile() 将 source 编译成代码或 AST(Abstract Syntax Trees) 对象。代码对象可以被 exec() 或 eval() 执行。source 可以是常规的字符串、字节字符串,或者 AST 对象。
filename 实参需要是代码读取的文件名;如果代码不需要从文件中读取,可以传入一些可辨识的值(经常会使用 '<string>'
)。
mode 实参指定了编译代码必须用的模式。如果 source 是语句序列,可以是 'exec';如果是单一表达式,可以是 'eval';如果是单个交互式语句,可以是 'single'。(在最后一种情况下,如果表达式执行结果不是 None 将会被打印出来。)
可选参数 flags 和 dont_inherit 控制应当激活哪个 编译器选项 以及应当允许哪个 future 特性。 如果两者都未提供 (或都为零) 则代码会应用与调用 compile() 的代码相同的旗标来编译。 如果给出了 flags 参数而未给出 dont_inherit (或者为零) 则会在无论如何都将被使用的旗标之外还会额外使用 flags 参数所指定的编译器选项和 future 语句。 如果 dont_inherit 为非零整数,则只使用 flags 参数 -- 外围代码中的旗标 (future 特性和编译器选项) 会被忽略。
编译器选项和 future 语句是由比特位来指明的。 比特位可以通过一起按位 OR 来指明多个选项。 指明特定 future 特性所需的比特位可以在 __future__
模块的 _Feature
实例的 compiler_flag 属性中找到。 编译器旗标 可以在 ast 模块中查找带有 PyCF_
前缀的名称。
optimize 实参指定编译器的优化级别;默认值 -1 选择与解释器的 -O 选项相同的优化级别。显式级别为 0 (没有优化;__debug__
为真)、1 (断言被删除, __debug__
为假)或 2 (文档字符串也被删除)。
如果编译的源码不合法,此函数会触发 SyntaxError 异常;如果源码包含 null 字节,则会触发 ValueError 异常。
如果您想分析 Python 代码的 AST 表示,请参阅 ast.parse()。
引发一个 审计事件 compile 附带参数 source, filename。
在 'single' 或 'eval' 模式编译多行代码字符串时,输入必须以至少一个换行符结尾。 这使 code 模块更容易检测语句的完整性。
在将足够大或者足够复杂的字符串编译成 AST 对象时,Python 解释器有可能因为 Python AST 编译器的栈深度限制而崩溃。
exec()
函数 exec() 可执行储存在字符串或文件中的 Python语句,相比于 eval,exec 可以执行更复杂的 Python 代码。exec 返回值永远为 None,除非你打印返回值。它是 Python 留给用户的一个接口。
语法:
python
exec(source, globals=None, locals=None, /)
参数说明:
在全局 globals() 和局部 locals() 命名空间的上下文中计算执行给定的 source 字符串表达式,返回值永远为 None。
source 可以是表示Python表达式的字符串,也可以是compile()返回的代码对象。
全局变量 globals 必须是字典,局部变量 locals 可以是任何映射类型,默认为当前全局变量和局部变量。
如果只提供 globals,则 locals 默认也是它。
python
exec('a=1\nb=1\nprint(a+b)')
# 2
exec('n=2+2')
n
# 4
此函数支持Python代码的动态执行。对象必须是字符串或代码对象。如果它是一个字符串,该字符串将被解析为一组 Python 语句,然后执行(除非出现语法错误)。如果它是一个代码对象,则只需执行它。在所有情况下,执行的代码都应作为文件输入有效(请参阅第节)文件输入 在参考手册中)。请注意,nonlocal、yield 和 return 语句不能在函数定义之外使用,甚至不能在传递给 exec() 函数的代码上下文中使用。返回值为 None。
解析器只接受 Unix 风格的行结束符。如果您从文件中读取代码,请确保用换行符转换模式转换 Windows 或 Mac 风格的换行符。
无论在什么情况下,如果省略了可选部分,代码将运行于当前作用域中。如果只提供了 globals,则必须为字典对象(而不能是字典的子类),同时用于存放全局变量和局部变量。如果提供了 globals 和 locals,则将分别用于全局变量和局部变量。locals 可以是任意字典映射对象。请记住,在模块级别,globals 和 locals 是同一个字典。如果 exec 获得两个独立的对象作为 globals 和 locals,代码执行起来就像嵌入到某个类定义中一样。
如果 globals 字典不包含 __builtins__
键值,则将为该键插入对内建 builtins 模块字典的引用。因此,在将执行的代码传递给 exec() 之前,可以通过将自己的 __builtins__
字典插入到 globals 中来控制可以使用哪些内置代码。
如果出现错误将引发一个 审计事件 exec 附带参数 code_object。
注意:
- 内置 globals() 和 locals() 函数各自返回当前的全局和本地字典,因此可以将它们传递给 exec() 的第二个和第三个实参。
- 默认情况下,locals 的行为如下面 locals() 函数描述的一样:不要试图改变默认的 locals 字典。如果您想在 exec() 函数返回时知道代码对 locals 的变动,请明确地传递 locals 字典。
需要注意的是,exec() 执行代码,不返回任何值(返回 None)。因此,不能在函数定义之外使用 return 和 yield 语句。
python
# 示例 1: 执行字符串代码块
code = "print('Hello, world!')"
exec(code)
# 示例 2: 指定全局和局部命名空间
globals_dict = {'x': 5}
locals_dict = {}
code = "y = x**2"
exec(code, globals_dict, locals_dict)
print(locals_dict['y']) # 输出: 25
# 示例 3: 执行代码对象
source_code = """
def greet(name):
print(f'Hello, {name}!')
greet('Alice')
"""
code_obj = compile(source_code, "<string>", "exec")
exec(code_obj)
python
x = 10
expr = """
z = 30
sum = x + y + z
print(sum)
"""
def func():
y = 20
exec(expr)
exec(expr, {'x': 1, 'y': 2})
exec(expr, {'x': 1, 'y': 2}, {'y': 3, 'z': 4})
func()
'''
60
33
34
'''
在expr语句中,有三个变量x,y,z,其中z值已给定,我们可以在exec()函数外指定x,y的值,也可以在exec()函数中以字典的形式指定x,y的值。在最后的语句中,我们给出了x,y,z的值,并且y值重复,exec()函数接收后面一个y值,且z值传递不起作用,因此输出结果为34.
ascii()
ascii(object) 与 repr() 类似返回一个字符串,表示对象的可打印 ASCII 码形式。非 ASCII 字符会用 \x、\u 和 \U 进行转义。
它的语法非常简单,ascii(object)
直接传入对象即可,对于对象中没 ascii 码的内容,会用 \x、\u 和 \U 进行转义
python
ascii(123)
# '123'
ascii('盖若')
# "'\\u76d6\\u82e5'"
Languages = ['pythØn','C++','Go']
ascii(Languages)
# "['pyth\\xd8n', 'C++', 'Go']"
# 字符
c = 'G'
# ASCII 码
a = 99
print(c, " 的ASCII 码为", ord(c))
print(a, " 对应的字符为", chr(a))
'''
G 的ASCII 码为 71
99 对应的字符为 c
'''
ASCII编码是1个字节,而Unicode编码通常是2个字节。
字母A用ASCII编码是十进制的65,二进制的01000001;而在Unicode中,只需要在前面补0,即为:00000000 01000001。
新的问题:如果统一成Unicode编码,乱码问题从此消失了。但是,如果你写的文本基本上全部是英文的话,用Unicode编码比ASCII编码需要多一倍的存储空间,在存储和传输上就十分不划算。
UTF-8 的编码规对于单字节的符号,字节的第一位设为0,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。
ord()和chr()
ord() 是将单个字符转 Unicode 码点,chr() 将 Unicode 码点转为字符, 它们两个互为逆函数。
ord(c)
:给定一个表示一个 Unicode 字符的字符串,返回一个表示该字符的 Unicode 码点的整数。例如 ord('a') 返回整数 97, ord('€') (欧元符号)返回 8364 。chr(i)
:返回表示 Unicode 码位为整数 i 的字符的字符串。例如,chr(97) 返回字符串 'a',chr(8364) 返回字符串 '€'。这是 ord() 的逆函数。实参的合法范围是 0 到 1,114,111(16 进制表示是 0x10FFFF)。如果 i 超过这个范围,会触发 ValueError 异常。ord(c)
:c 为一个字符串,返回一个整数,这个整数为 Unicode 码位编号chr(i)
:i 一个整数(有上述范围限制),返回码位编号为这个整数的 Unicode 字符串
python
ord('a')
# 97
ord('¥') # 人民币符号
# 165
ord('美国') # 多个字符
# TypeError: ord() expected a character, but string of length 2 found
chr(97)
# 'a'
chr(165)
# '¥'
chr(1_114_112) # 大于 1_114_111
# ValueError: chr() arg not in range(0x110000)
bin()
将整数转变为以"0b"前缀的二进制字符串。结果是一个合法的 Python 表达式。如果 i 不是 Python 的 int 对象,它必须定义 __index__()
方法,以便返回整数值。如果未指定整数,则会引发TypeError异常,突出显示无法解释为整数的类型。
python
bin(3)
# '0b11'
bin(-10)
# '-0b1010'
若要控制是否显示前缀"0b",可以采用以下两种方案:
python
format(14, '#b'), format(14, 'b')
# ('0b1110', '1110')
f'{14:#b}', f'{14:b}'
# ('0b1110', '1110')
index()
__index()__类似于__str()__方法,__str()__输出对象的字符串表示,__index()__方法输出对象的整数表示,可以自
定义对象的 __index()__
方法:
python
class Quantity:
apple = 1
orange = 2
grapes = 2
def __index__(self):
return self.apple + self.orange + self.grapes
bin(Quantity())
# 0b101
在这里,我们向 bin()
方法发送了一个 Quantity 类的对象。即使对象数量不是整数, bin()
方法也不会引发错误。这是因为我们实现了返回整数(水果数量之和)的 __index__()
方法。然后将该整数提供给 bin()
方法。
hex()
hex() 用于将10进制整数转换成16进制,以「0x」为前缀字符串形式表示。它接受的是一个十进制的数字,如果参数不是 Python int 对象,则必须定义返回整数的 __index__()
方法。
hex() 函数接受单个参数。
python
hex(number, /)
参数是一个整数(int 对象或必须定义返回整数的 index() 特殊方法)。
它将整数转换为字符串形式的相应十六进制数并返回。返回的十六进制字符串以前缀「0x」开头,表示其为十六进制形式。
如果要获取浮点数的十六进制字符串形式,请使用 float.hex() 方法。
python
hex(255)
# '0xff'
hex(-42)
# '-0x2a'
hex(12648430)
# '0xc0ffee'
'%#x' % 255, '%x' % 255, '%X' % 255
# ('0xff', 'ff', 'FF')
format(255, '#x'), format(255, 'x'), format(255, 'X')
# ('0xff', 'ff', 'FF')
f'{255:#x}', f'{255:x}', f'{255:X}'
# ('0xff', 'ff', 'FF')
oct()
内置函数 oct() 将一个整数转变为一个前缀为「0o」的八进制字符串。必须传入一个整数。
语法为 oct(x)
,参数:
- 整数(二进制、十进制或十六进制)
- 如果 x 不是 Python 的 int 对象,那它需要定义
__index__()
方法返回一个整数。
python
oct(8)
# '0o10'
oct(-56)
# '-0o70'
oct(0b101)
# '0o5'
oct(0XA)
# '0o12'
python
class Person:
age = 23
def __index__(self):
return self.age
def __int__(self):
return self.age
person = Person()
print('The oct is:', oct(person))
# The oct is: 0o27
Person 类实现了 __index__()
和 __int__()
,因此我们可以此对象上使用 oct()。
注意,为了兼容性,推荐 __index__()
和 __int__()
输出结果。
若要将整数转换为八进制字符串,并可选择是否带有"0o"前缀,可采用如下方法:
python
'%#o' % 10, '%o' % 10
# ('0o12', '12')
format(10, '#o'), format(10, 'o')
# ('0o12', '12')
f'{10:#o}', f'{10:o}'
# ('0o12', '12')
int()
内置函数 int() 是构造方法,它可以将数字或字符串转换为整型数字。它还可以通过 base 参数,可以转化指定进制的数字。
int() 有两种使用形式,分别是:
python
int([x]) -> integer
int(x, base=10) -> integer
它们都会返回一个基于数字或字符串 x 构造的整数对象,或者在未给出参数时返回 0。
在第一种情况下,可以传入一个 字符, 类字节对象(bytes-like object)或者一个实数,第二种情况可以传入一个 base 变量,来指定返回数字的进制,默认为 10,是我们日常使用的 10 进制。
如果 x :
- 定义了
__int__()
,int(x) 将返回x.__int__()
定义的值。 - 定义了
__index__()
,它将返回x.__index__()
定义的的值。 - 定义了
__trunc__()
,它将返回x.__trunc__()
的值。
以上几个如果定义了两种及三种,会按以上顺序依次返回排在前边的值。
对于浮点数,它将截断小数位,舍去所有小数位上的数字。
如果 x 不是数字,或者有 base 参数,x 必须是字符串、bytes、表示进制为 base 的 整数字面值 的 bytearray 实例。该文字前可以有 + 或 - (中间不能有空格),前后可以有空格。
一个进制为 n 的数字包含 0 到 n-1 的数,其中 a 到 z (或 A 到 Z )表示 10 到 35。默认的 base 为 10 ,允许的进制有 0、2-36。
2、8、16 进制的数字可以在代码中用 0b/0B 、 0o/0O 、 0x/0X 前缀来表示。进制为 0 将安照代码的字面量来精确解释,最后的结果会是 2、8、10、16 进制中的一个。所以 int('010', 0)
是非法的,但 int('010')
和 int('010', 8)
是合法的。
默认为 10 进制:
python
# integer
int(123)
# float
int(123.23)
# string
int('123')
# 以上均输出:
# 123
# 字符中可包含分组下划线
int('323_133')
# 323133
用于十进制、八进制和十六进制
python
# 二进制 binary 0b or 0B
int('1010', 2)
int('0b1010', 2)
# 八进制 octal 0o or 0O
int('12', 8)
int('0o12', 8)
# 十六进制 hexadecimal
int('A', 16)
int('0xA', 16)
# 以上均输出:
# 10
int()
在内部, int() 作为方法可以调用对象的 __int__()
方法。因此,即使对象不是个数字,也可以将对象转换为整数对象。可以通过重写类的 __index__()
和 __int__()
方法返回一个数字。
这两个方法返回的值应该与较早版本的 Python 使用的 __int__()
相同,而较新版本使用的是 __index__()
方法。
python
class Person:
age = 23
def __index__(self):
return self.age
def __int__(self):
return self.age
person = Person()
int(person)
# 23
如果无法转换,则抛出 ValueError
异常:
python
try:
five = "five"
print(int(five))
except ValueError as e:
print(e)
# invalid literal for int() with base 10: 'five'
bool()
bool() 函数的作用是使用标准的真值测试过程将值转换为布尔值(True 或 False)
class bool([x])
向 bool()
传递值不是强制性的,可以不传值。如果不传递值, bool()
将返回 False。通常情况下,只接受一个参数值。
bool()
将返回:
- False 不值或者假值
- True 值为真值
Python中的真假值
在 Python 中,以下值被视为 假值:
- None
- False
- 任何数字类型的零,例如,0,0.0,0j
- 空序列,例如 (), [], ''
- 空映射,例如 {}
- 类的对象特殊方法
__bool__()
返回 False 或是定义了__len__()
方法且返回 0
除这些值外的所有其他值均视为真。
python
test = []
print(test,'is',bool(test))
test = [0]
print(test,'is',bool(test))
test = 0.0
print(test,'is',bool(test))
test = None
print(test,'is',bool(test))
test = True
print(test,'is',bool(test))
test = 'Easy string'
print(test,'is',bool(test))
'''
[] is False
[0] is True
0.0 is False
None is False
True is True
Easy string is True
'''
bool()
__bool()__与前面的__str()__和__index()__类似,定义了对象的布尔表示,可以自定义对象的表示:
python
class Student(object):
def __init__(self, name, age):
self.name = name
self.age = age
# 如果是成年为 True
def __bool__(self):
return self.age >= 18
me = Student('小明', 16)
bool(me)
# False
him = Student('大明', 18)
bool(him)
# True
# 用于逻辑判断
if Student('大明', 18):
print('成年人')
else:
print('未成年人')
# 成年人
complex()
complex() 用于创建一个复数对象。可以传入一个在加减号周围无空格的字符串或者两个数字来代表复数的实部和虚部。
有两种参入方法:
python
complex(x:str)
complex(real=0, imag=0)
返回值为 real + imag*1j 的复数,或将字符串或数字转换为复数。有两种传参方法。
第一种参数形式:
如果第一个形参是字符串,则它被解释为一个复数,并且函数调用时必须没有第二个形参。
对于一个普通 Python 对象 x,complex(x) 会委托给 x.__complex__()
。 如果 __complex__()
未定义则将回退至 __float__()
。 如果 __float__()
未定义则将回退至 __index__()
。
注意,当从字符串转换时,字符串在 + 或 - 的周围必须不能有空格。
例如 complex('1+2j')
是合法的,但 complex('1 + 2j')
会触发 ValueError 异常。
第二种参数形式:
第二个形参不能是字符串。每个实参都可以是任意的数值类型(包括复数)。如果省略了 imag,则默认值为零,构造函数会像 int 和 float 一样进行数值转换。如果两个实参都省略,则返回 0j。
python
complex('1-2j')
# (1-2j)
complex(-2j)
# (-0-2j)
foo = complex(1, 2)
foo
# (1+2j)
foo.conjugate()
# (1-2j)
foo.real # 1.0
foo.imag # 2.0
byte()
bytes() 可以生成字节序列(又叫字节串),字节序列是一个不可变序列包含范围为 0 <= x < 256 的整数,bytes 是 bytearray 的不可变版本------带有同样不改变序列的方法,支持同样的索引、切片操作。因此,构造函数的实参和 bytearray() 相同。
语法构成是:
class bytes([source[, encoding[, errors]]])
它是一个类,可以初始化,生成一个字节序列对象。
接受三个可选参数:
- source:用于初始化字节串的源数据
- encoding:如果 source 是字符串,则可指定字符串的编码
- errors:如果 source 是字符串,则编码转换失败时要采取的操作
source 参数可以通过以下方式初始化字节数组:
bytes(iterable_of_ints)
-> bytes,创建一个大小等于 iterable 数量的数组,并初始化为 iterable 元素,必须是0<=x<256
之间整数的 iterablebytes(string, encoding[, errors])
-> bytes,将字符串转换为字节串,还必须提供编码和可选错误处理,见 str.encode()bytes(bytes_or_buffer)
-> bytes_or_buffer 的不可变副本,传入一个 Object,对象的只读缓冲区将用于初始化字节数组bytes(int)
-> 长度为 int 的初始化(\x00
)字节对象bytes()
-> 空的 bytes 对象
总结,可以从以下内容构造一个不变的字节数组:
- 从 range(256) 中迭代出
- 使用指定编码进行编码的文本字符串
- 实现缓冲区API的任何对象
- 整数
python
# 限制为 0 <= x < 256
bytes(10) # 指定长度的以零值填充的 bytes 对象
# b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
bytes(range(20)) # 通过由整数组成的可迭代对象
# b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b...'
obj = (1, 2)
bytes(obj) # 通过缓冲区协议复制现有的二进制数据
# b'\x01\x02'
bytes('hello', 'utf-8')
# b'hello'
bytearray()
bytearray()返回一个新的 bytes 数组。 bytearray 类是一个可变序列,包含范围为 0 <= x < 256 的整数。它有可变序列大部分常见的方法,见 可变序列类型 的描述;同时有 bytes 类型的大部分方法,参见 bytes 和 bytearray 操作。
语法:
class bytearray([source[, encoding[, errors]]])
可选形参 source 可以用不同的方式来初始化数组:
- 如果是一个 string,您必须提供 encoding 参数(errors 参数仍是可选的);bytearray() 会使用 str.encode() 方法来将 string 转变成 bytes。
- 如果是一个 integer,会初始化大小为该数字的数组,并使用 null 字节填充。
- 如果是一个遵循 缓冲区接口 的对象,该对象的只读缓冲区将被用来初始化字节数组。
- 如果是一个 iterable 可迭代对象,它的元素的范围必须是 0 <= x < 256 的整数,它会被用作数组的初始内容。
- 如果没有实参,则创建大小为 0 的数组。
python
bytearray() # 创建一个空实例
# bytearray(b'')
bytearray(10) # 创建一个指定长度的以零值填充的实例
# bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
bytearray(range(20)) # 通过由整数组成的可迭代对象
bytearray(b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b...')
bytearray(b'Hi!') # 通过缓冲区协议复制现有的二进制数据
# bytearray(b'Hi!')
classmethod() 和staticmethod()
内置函数 classmethod() 是一个装饰器,它可以将一个方法封装为一个类的方法。修饰符对应的函数不需要实例化,不需要 self 参数,但第一个参数需要是表示自身类的 cls 参数,可以来调用类的属性,类的方法,实例化对象等。
内置函数 staticmethod() 是一个装饰器,它可以将一个方法封装为一个静态方法,该方法不强制要求传递参数。
语法为:
python
classmethod(function) -> method
# or
@classmethod
def func(cls, args...):
pass
staticmethod(function) -> method
# or
@staticmethod
def func(cls, args...):
pass
将函数转换为类方法。类方法接收类(一般作 cls)作为隐式第一个参数,就像实例方法接收实例(self)一样。staticmethod 将方法转换为静态方法。静态方法不会接收隐式的第一个参数。
要声明类方法、静态方法,请使用以下习惯用法:
python
class C:
@classmethod
def f(cls, arg1, arg2, ...):
...
class C:
@staticmethod
def f(cls, arg1, arg2, ...):
...
类方法可以在类(例如 C.f()
)或实例(例如 C().f()
)使用。实例被忽略,但其类除外。
如果为派生类调用类方法,则派生类对象将作为隐含的第一个参数传递。
静态方法既可以由类中调用(如 C.f()),也可以由实例中调用(如C().f()
)。此外,还可以作为普通的函数进行调用(如 f()
)。
类方法不同于 C++ 或 Java 静态方法。如果您想要静态方法,可以使用内置的 @staticmethod
。
像所有装饰器一样,也可以像常规函数一样调用 staticmethod ,并对其结果执行某些操作。比如某些情况下需要从类主体引用函数并且您希望避免自动转换为实例方法。对于这些情况,请使用此语法:
python
def regular_function():
...
class C:
method = staticmethod(regular_function)
无论何时通过将工厂方法实现为类方法来派生类,它都可以确保正确创建派生类的实例。
您可以为上面的示例创建一个静态方法,但它创建的对象将始终被硬编码为基类。
但是,当您使用类方法时,它会创建派生类的正确实例。
python
from datetime import date
# random Person
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
@staticmethod
def fromFathersAge(name, fatherAge, fatherPersonAgeDiff):
return Person(name, date.today().year - fatherAge + fatherPersonAgeDiff)
@classmethod
def fromBirthYear(cls, name, birthYear):
return cls(name, date.today().year - birthYear)
def display(self):
print(self.name + "'s age is: " + str(self.age))
class Man(Person):
sex = 'Male'
man = Man.fromBirthYear('John', 1985)
print(isinstance(man, Man))
man1 = Man.fromFathersAge('John', 1965, 20)
print(isinstance(man1, Man))
'''
True
False
'''
在这里,使用静态方法创建类实例需要我们在创建过程中硬编码实例类型。这显然会给人与人之间的继承带来问题。
fromFathersAge 方法不返回 Man 对象,而是返回其基类 Person 的对象。
这违反了 OOP 范式。将类方法用作 fromBirthYear 可以确保代码的 OOP 性,因为它将第一个参数作为类本身并调用其工厂方法。
静态方法和类方法的区别在于:
- 静态方法对类一无所知,只处理自己的参数。
- 类方法与类一起工作,因为它的参数始终是类本身。
property()
内置函数(类) property() 一般以装饰器 @property 的形式出现,我们可以使用它来创建只读属性,会将方法转换为相同名称的只读属性,可以与所定义的属性配合使用,这样可以防止属性被修改。同时还可以指定获取、设置、删除的方法。
语法如下:
python
class property(fget=None, fset=None, fdel=None, doc=None)
参数:
- fget:用于获取属性值的函数
- fset:用于设置属性值的函数
- fdel:用于删除属性的函数
- doc:docstring,文档字符串
其中,如果 doc 参数给出,doc 将成为该 property 属性的文档字符串。 否则该 property 将拷贝 fget 的文档字符串(如果存在)。 这令使用 property() 作为 decorator 来创建只读的特征属性可以很容易地实现:
python
class Parrot:
def __init__(self):
self._voltage = 100000
@property
def voltage(self):
"""Get the current voltage."""
return self._voltage
以上 @property 装饰器会将 voltage() 方法转化为一个具有相同名称的只读属性的 "getter",并将 voltage 的文档字符串设置为 "Get the current voltage."
特征属性对象具有 getter, setter 以及 deleter 方法,它们可用作装饰器来创建该特征属性的副本,并将相应的访问函数设为所装饰的函数。 这最好是用一个例子来解释:
python
class C:
def __init__(self):
self._x = None
@property
def x(self):
"""I'm the 'x' property."""
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
python
class Shijin:
def __init__(self, jin, liang):
self.total_liang = jin * 10 + liang
# 获取斤
@property
def jin(self):
return self.total_liang // 10
# 获取和设置两
@property
def liang(self):
return self.total_liang % 10
@liang.setter
def liang(self, new_liang):
self.total_liang = 10 * self.jin + new_liang
def __repr__(self):
return "{}斤{}两".format(self.jin, self.liang)
jiaozi = Shijin(3, 2)
jiaozi
# 3斤2两
jiaozi.liang = 4
jiaozi
# 3斤4两
jiaozi.liang += 6
jiaozi
# 4斤0两
jiaozi.jin += 10
# AttributeError: can't set attribute 'jin'
jiaozi.jin
# 4
id()
id(object) 返回对象的"标识值"。该值是一个整数,在此对象的生命周期中保证是唯一且恒定的。两个生命期不重叠的对象可能具有相同的 id() 值。在 CPython 实现中这就是内存中对象的地址。对象的 id 对于 Python 缓存这些变量的值很有用。这种使用 id() 检索缓存值的机制使 Python 的性能更好!这在多个变量引用同一对象的情况下也有帮助。注:本页的代码 id 结果不会与你执行相同代码时相同。
语法为 id(object)
,由于在 Python 一切皆为对象,所有的对象都可以传入 id() 中。示例:
python
a = 1
id(a)
# 140232072763696
id(1)
# 140232072763696
id(lambda x: x+1)
# 140359443164176
def hello():
print('hello!')
id(hello)
# 140231834272240
# 未定义的名称
id(test)
# NameError: name 'test' is not defined
上例中,a
与 1
的 id() 返回值相同,则证明它们在内存中的地址(memory address of the Python object)相同。这个地址只在一个生命周期内是相同的,如果重启解释器(交互窗口)或者重新执行,则会改变。
对于可变对象,内容即使相同它们的内存地址也不同。请记住,缓存只能在不可变的 Python 对象上工作,如整数、字符串和浮点。元组、列表等是可变对象,因此缓存在这里不起作用!
python
class Student:
data = 0
e1 = Student()
e2 = Student()
print(id(e1))
print(id(e2))
# 140231806582208
# 140231806582688
上边说了,如果对象的内容相同,Python 不会新开辟空间存储,而是指向同一个对象,下边的两个对象 id() 是相同的!
判断两个对象是否相等,可以用 is 操作:
python
a is b
# True
它就等于:
python
id(a) == id(b)
两个对象如何才能相等要比我们想象的复杂很多,但核心的方法是重写 __eq__
方法,这个方法返回 True,则表示两个对象相等,否则,就不相等。相反的,如果两个对象不相等,则重写 __ne__
方法。
默认情况下,如果你没有实现这个方法,则使用父类(object)的方法。父类的方法比较是的两个对象的ID(可以通过id方法获取对象ID),也就是说,如果对象的ID相等,则两个对象也就相等。因此,我们可以得知,默认情况下,对象只和自己相等。
enumerate()
enumerate() 方法将计数器添加到 iterable ,每个元素连同计数(或者称为索引)组成元组,并返回它(枚举对象 enumerate object)。
enumerate() 的语法为:
enumerate(iterable, start=0)
enumerate() 方法采用两个参数:
- iterable - 可迭代的序列
- start (可选) - 从这个数字开始计数,默认为 0
返回:
方法将计数器添加到 iterable 并返回它。返回的对象是枚举对象。可以分别使用 list() 和 tuple() 方法将枚举对象转换为 list 和 tuple。例如:
python
seasons = ['Spring', 'Summer', 'Fall', 'Winter']
list(enumerate(seasons))
# [(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')]
list(enumerate(seasons, start=1))
# [(1, 'Spring'), (2, 'Summer'), (3, 'Fall'), (4, 'Winter')]
等价于:
python
def enumerate(sequence, start=0):
n = start
for elem in sequence:
yield n, elem
n += 1
一般在 for 循环中为列表增加索引,方便让这些索引数字介入计算:
python
l = ['Alice', 'Bob', 'Charlie']
# 一般的循环
for name in l:
print(name)
# Alice
# Bob
# Charlie
# 可以按索引编号和元素的顺序获取
for i, name in enumerate(l):
print(i, name)
# 0 Alice
# 1 Bob
# 2 Charlie
上例那样,默认情况下,enumerate() 函数的索引从 0 开始。如果想要从 0 以外的数值开始,则对 enumerate() 函数的第二参数指定任意的开始数值。如从 1 开始时:
python
for i, name in enumerate(l, 1):
print(i, name)
# 1 Alice
# 2 Bob
# 3 Charlie
想制作序列号的字符串时很方便。比起从 1 开始 i+1,用 enumerate() 函数的第二参数指定开始数值更智能。
python
for i, name in enumerate(l, 1):
print('{:03}_{}'.format(i, name))
# 001_Alice
# 002_Bob
# 003_Charlie
在 enumerate() 函数中没有指定增量step的参数,但是可以实现如下:
python
step = 3
for i, name in enumerate(l):
print(i * step, name)
# 0 Alice
# 3 Bob
# 6 Charlie
hash()
内置函数 hash() 可以返回对象的哈希值。哈希值又称散列值,它是一个固定大小的整数,用于标识对象的特定值。hash() 又称散列算法、哈希函数,是一种从任何一种数据中创建小的数字"指纹"的方法。
python
hash('你好')
# 2439288928012809752
hash(123)
# 123
hash(123.0)
# 123
hash(range(2))
# 7853416581674910768
hash((1, 2))
# -3550055125485641917
hash([1, 2])
# TypeError: unhashable type: 'list'
如果对象实现了自己的 __hash__()
方法,hash() 根据机器的字长来截断返回值。自己要实现 hash 功能,可以在对象中来重写 __hash__()
来自定义 hash 值。
自己定义的 __hash__()
要返回一个整数,必须同时实现 __eq__()
和 __hash__()
,下面是正确是正确的的例子:
python
class Person:
def __init__(self, age, name):
self.age = age
self.name = name
def __eq__(self, other):
return self.age == other.age and self.name == other.name
def __hash__(self):
print('The hash is:')
return hash((self.age, self.name))
person = Person(23, 'Adam')
hash(person)
# -1253368434537217793
如无必要,对象不必自己实现 __eq__()
方法,因为它是默认为所有对象创建的。
memoryview()
内置函数 memoryview() 返回给定参数的内存查看对象(memory view)。memoryview 对象允许 Python 代码访问支持缓冲区协议的对象的内部数据,而无需复制。
语法:
python
class memoryview(object)
创建一个引用 obj 的 memoryview。obj 必须支持缓冲协议。支持缓冲区协议的内置对象包括字节bytes和字节数组bytearray。
python
v = memoryview(b'abcefg')
v[1]
# 98
v[-1]
# 103
v[1:4]
# <memory at 0x7f3ddc9f4350>
bytes(v[1:4])
# b'bce'
iter()
内置函数 iter() 用来生成迭代器,它可以将字符串、列表、元组等序列转换为可迭代器,供 for 循环、next() 等进行迭代操作。
iter的语法有两种:
python
iter(iterable) -> iterator
iter(callable, sentinel) -> iterator
从对象中获取迭代器。在第一种形式中,参数必须提供自己的迭代器,或者是一个序列。
在第二种形式中,将调用 callable,直到它返回 sentinel。
如果只传入一个参数就按第一种解释,如果传入两个就按第二种方法解释。
参数分别是:
- 第一种方法:
- iterable:可迭代对象或者一个序列,如列表、字符串、字典等,直接将这个可迭代对象转为迭代器。或者它必须支持 sequence 协议(
__getitem__()
方法,整数参数从0开始),否则引发 TypeError。
- iterable:可迭代对象或者一个序列,如列表、字符串、字典等,直接将这个可迭代对象转为迭代器。或者它必须支持 sequence 协议(
- 第二种方法:
- callable:可调用对象,一个函数的名称。每次调用其
__next__()
方法,不带任何参数,如果返回的值等于 sentinel,则将引发 StopIteration,否则将返回该值。见示例中关于读取文件有用法。 - sentinel:哨兵(见 object() 中相关概念的介绍),当迭代值为此值时抛出 StopIteration。
- callable:可调用对象,一个函数的名称。每次调用其
返回一个迭代器对象。
对于一个对象,要支持 iter() 函数,需要在内部实现 __iter__()
特殊方法。
python
from functools import partial
with open('mydata.db', 'rb') as f:
for block in iter(partial(f.read, 64), b''):
process_block(block)
由于 callable 不能带任何参数,先用偏函数处理。以上代码当数据读取为空时引发 StopIteration(在 for 是停止)。
reversed()
内置函数 reversed() 会返回给定序列的反向迭代器,如果需要查看返回的这个迭代器的内容,需要迭代输出或者转换为列表、元组等数据类型。
语法是
reversed(seq)
seq 可以是:
- tuple, string, list 或 range 等
- 具有
__reversed__()
方法的对象 - 支持该序列协议(具有从 0 开始的整数类型参数的
__len__()
方法和__getitem__()
方法)。
可以对任意实现了 __reverse__()
特殊方法的对应使用 reversed()。
python
class Vowels:
vowels = ['a', 'e', 'i', 'o', 'u']
def __reversed__(self):
return reversed(self.vowels)
v = Vowels()
list(reversed(v))
# ['u', 'o', 'i', 'e', 'a']
next()
可以从迭代器中检索下一项,如果给定默认值,则在迭代器耗尽时返回此默认值,否则将引发 StopIteration 异常
语法是:
next(iterator[, default])
参数说明
- iterator,必须,它必须是一个迭代器(或者生成器),可由 iter() 将序列转换而来
- default,可选,如果迭代器的元素耗尽(消费完)则返回此值,否则引发 StopIteration 异常
作用是从迭代器返回下一项。如果给定了默认值,并且迭代器已耗尽,则返回它,而不是引发 StopIteration。
python
it = iter([1, 2, 3])
while True:
try:
x = next(it)
print(x)
except StopIteration:
print('Over')
break
round()
round() 返数字舍入到小数点后指定位精度的值。 如果不指定小数位精度,则返回最接近输入值的整数。这是一个数字修约操作。数值修约,是指在进行具体的数字运算前,按照一定的规则确定一致的位数,然后舍去某些数字后面多余的尾数的过程。
语法为 round(number, ndigits=None)
期中 ndigits 是一个,将数字舍入到给定的小数精度。如果省略 ndigits 或无 ndigits,则返回值为整数。否则,返回值的类型与数字相同。ndigits 可能是负的。
对于支持 round() 方法的内置类型,结果值会舍入至最接近的 10 的负 ndigits 次幂的倍数;如果与两个倍数同样接近,则选用偶数。因此,round(0.5) 和 round(-0.5) 均得出 0 而 round(1.5) 则为 2。ndigits 可为任意整数值(正数、零或负数)。如果省略了 ndigits 或为 None ,则返回值将为整数。否则返回值与 number 的类型相同。
对于一般的 Python 对象 number, round 将委托给 number.__round__
,可以类中实现此特殊方法。
对浮点数执行 round() 的行为可能会令人惊讶:例如,round(2.675, 2) 将给出 2.67 而不是期望的 2.68。 这不算是程序错误:这一结果是由于大多数十进制小数实际上都不能以浮点数精确地表示。例如当十进制 2.675转换为二进制浮点数时,它再次被二进制近似值替换,其精确值为:
2.67499999999998223643160599749535322183310546875
因此,四舍五入为 2.67,而不是 2.68。
浮点数在计算机硬件中表示为以 2 为基数(二进制)的小数。由于这个 0.1 无法精确表示 1/10 的值而这个 0.3 也无法精确表示 3/10 的值,使用 round() 函数进行预先舍入也是没用的:
虽然这些小数无法精确表示其所要代表的实际值,round() 函数还是可以用来"事后舍入",使得实际的结果值可以做相互比较:
python
round(.1 + .1 + .1, 10) == round(.3, 10)
# True
如果你需要比较高的精度,考虑使用 decimal
模块,它设计用于浮点运算:
python
from decimal import Decimal
# normal float
num = 2.675
round(num, 2)
# 2.67
# 使用 decimal.Decimal (将浮点作为字符串传递以提高精度)
num = Decimal('2.675')
round(num, 2)
# 2.68
可以自己定义一个方法。一种方法是将移位值加上0.5,然后用 math.floor()
(地板除)。原理是:
- 如果移位值的第一位小数位数小于5,则加 0.5 不会改变移位值的整数部分,因此下限等于整数部分
- 如果小数点后的第一位大于或等于5,则加0.5将使移位值的整数部分增加1,因此下限等于这个较大的整数
简单说就是利用了 int 直接截断的特性,测试如下(以保留两个小数为例):
python
int(0.115*100+0.5)/100
# 0.12
int(0.125*100+0.5)/100
# 0.13
python
import math
def round_half_up(n, decimals=0):
multiplier = 10 ** decimals
return math.floor(n*multiplier + 0.5) / multiplier
round_half_up(0.625, 2)
# 0.63
还有一种办法是用 decimal
模块的 quantize 方法(强烈推荐):
python
from decimal import Decimal
Decimal(0.625).quantize(Decimal("0.01"), rounding = "ROUND_HALF_UP")
# Decimal('0.63')
其他方法:
python
import decimal
Decimal('1.115').quantize(Decimal('0.00'), rounding=decimal.ROUND_HALF_UP)
# Decimal('1.12')
Decimal('0.125').quantize(Decimal('0.00'), rounding=decimal.ROUND_HALF_UP)
# Decimal('0.13')
可以再将 Decimal 对象再转为浮点型。
python
from decimal import Decimal
def round_half_up(n, d):
assert d >= 1, 'd 不能小于1'
assert type(d) == int, 'd 必须为整型'
num = Decimal(str(n))
num = num.quantize(Decimal('0.'+'0'*d),
rounding="ROUND_HALF_UP")
return float(num) if d>1 else int(num)
round_half_up(0.625, 2)
# 0.63
round_half_up(3.14, 1)
# 3
除非对精确度没什么要求,否则尽量避开用round()函数。近似计算我们还有其他的选择:
- 使用math模块中的一些函数,比如 math.ceiling(天花板除法)
- python 自带整除,//,还有 div 函数。
- 字符串格式化可以做截断使用,例如 "%.2f" % value(保留两位小数并变成字符串)
- 当然,对浮点数精度要求如果很高的话,请用 decimal 模块。
abs()
返回一个数的绝对值,常用在数学计算中
语法形式是 abs(x)
必传且只能传入一个参数,这个参数必须是一个数字。它可以接受的数据包括:
- 整型 integer
- 浮点型 floating number
- 复数 complex number
- 任何实现了
__abs__()
的对象
返回一个数的绝对值。具体情况下:
- 整型 integer:返回整数绝对值
- 浮点型 floating number:返回浮点绝对值
- 复数 complex number:复数的模(magnitude)
注:复数的模(magnitude)是该复数与共轭复数的乘积的正平方根,就是 (a^2+b^2) 开平方根。
python
# 整数的情况
abs(10)
# 10
abs(-10)
# 10
# 浮点型的情况
abs(-3.14)
# 3.14
# 复数的情况
complex1 = (5 - 6j)
abs(complex1)
# 7.810249675906654
complex2 = (3 - 4j)
abs(complex2)
# 5.0
abs(1j)
#1.0
构建新的数据对象时可以通过实现 __abs__()
让内置的 abs 操作数据。
python
class Student(object):
"""这是一个学生类"""
def __init__(self, name):
self.name = name
def __abs__(self):
print(f'{self.name} 绝对棒!')
lily = Student('lily')
abs(lily)
# lily 绝对棒!
divmod()
内置函数 divmod() 返回这两个数字的商和余数(模)组成的元组,即 (x//y, x%y)
。
语法非常简单,直接传入两个要计算的数字:
python
divmod(x, y, /)
他们不能为负数,y 不能为 0。
以两个(非复数)数字为参数,在作整数除法时,返回商和余数。
若操作数为混合类型,则适用二进制算术运算符的规则。对于整数而言,结果与 (a // b, a % b)
相同。
python
divmod(10, 3)
# (3, 1)
divmod(10, -1)
# (-10, 0)
divmod(10, 0.1)
# (99.0, 0.09999999999999945)
divmod(10, 0)
# ZeroDivisionError: integer division or modulo by zero
divmod(0, 10)
# (0, 0)
pow()
内置函数 pow() 可以求一个数的 n 次幂,即返回 x^y(x 的 y 次方) 的值。
语法如下:
python
pow(base, exp, mod=None)
返回 base**exp
的值,如果 mod 有传入,则返回 base**exp % mod
先求 n 次幂,再取模。
某些类型,例如 int,在使用三参数形式调用时能够使用更高效的算法。
参数必须是数字类型。对于混合操作数类型,二进制算术运算符的强制规则适用。
对于整型操作数,结果的类型与操作数的类型相同(强制后),除非第二个参数为负;在这种情况下,所有参数都转换为float,并传递一个float结果。例如,pow(10,2)返回100,但pow(10,-2)返回0.01。
对于int或float类型的负基和非整数指数,将给出一个复杂的结果。例如,pow(-9,0.5) 返回一个接近3j的值。
对于 int 操作数 base 和 exp,如果给出 mod,则 mod 必须为整数类型并且 mod 必须不为零。 如果给出 mod 并且 exp 为负值,则 base 必须相对于 mod 不可整除。 在这种情况下,将会返回 pow(inv_base, -exp, mod),其中 inv_base 为 base 的倒数对 mod 取余。
下面的例子是 38 的倒数对 97 取余:
python
pow(38, -1, mod=97)
# 23
23 * 38 % 97 == 1
# True
它与 **
操作以及 math.pow() 的区别为:
- pow() 相同于
**
操作 - math.pow() 计算的结果是浮点
- math.pow() 不支持第三个参数进行取模
可以对对象实现 __pow__(), __rpow__() or __ipow__()
方法来支持 power 操作。