内置函数id(obj)
返回一个对象的标识,它是一个整数值。
#a和b是同一个对象
a is b
#a和b具有相同的值,由具体实现决定
a == b
type(obj)
可以检查对象的类型,对象的类型在解释器当中应该只需要存在一个,因此可以使用type(x) is type(y)
判断两个对象的类型是否完全一样。
检查类型还可以使用内置函数:isinstance(obj,classname)
,它能够识别继承体系。
所有对象都有引用计数,给一个对象分配一个新名称,或是将对象放入一个容器中,都会增加引用计数。使用del obj
或者超出对象作用域范围,计数就会减少。使用sys.getrefcount()
可以获得对象当前的引用计数。
当对象的引用计数为0,它将被垃圾回收机制处理掉。
如果两个对象存在循环引用,那么它们的引用计数永远大于0。解释器会周期性地检查不可访问对象并删除它们。
一个常见的安全问题:
a=[1,2,3]
b=a
b[1]=4
print(a) #1,4,3
对于容器对象,Python提供2种复制:浅复制,深复制。浅复制创建一个新的容器对象,但它的元素仍然原容器元素的引用。深复制将创建一个新对象,并递归地复制所有元素。
浅复制
a=[1,2,[3,4]]
b=list(a) #浅复制
b[1]=3
print(a) # [1, 2, [3, 4]]
b[2][0]=1
print(a) #[1, 2, [1, 4]]
深复制
import copy
a=[1,2,[3,4]]
b=copy.deepcopy(a)
b[1]=3
print(a) # [1, 2, [3, 4]]
b[2][0]=1
print(a) # [1, 2, [3, 4]]
Python所有对象都属于first class
,所有能用标识符命名的对象都具有平等的身份,这还意味着能被命名的对象都可以当作数据处理。
line='good,100,490.0'
fieldTypes=[str,int,float]
rawFields=line.split(",")
fields=[]
for i in range(len(rawFields)):
val=rawFields[i]
ty=fieldTypes[i]
fields.append(ty(val)) #str(x),int(x),float(x)
print(fields) #['good', 100, 490.0]
在这个例子中,函数str(x)
的名称str
被当作数据处理。
同一个类型包含一些通用的操作:
None
int,long,float,complex,bool
dict
set ,frozenset
序列包含一系列的通用操作。
None
表示一个null
对象。如果一个方法没有返回值,那么它会返回None
对象,它的布尔求值是False
.
Python的数值类型都是不可变对象。
Python3中int,long
已经整合为一种类型了。
兼容措施举例:Python为了兼容复数运算,给整数增加了属性y.real,y.imag
。
decimal
:十进制算术模块
fractions
:有理数模块
序列有以下几种:
str
,不可变list
,可变tuple
,不可变range()
对象所有序列均支持以下操作
s='abcdefghijk'
'ab' in s #如果s中的某项等于x
'ab' not in s
s+t # s与t拼接
s*n #s与自身进行n次拼接,它是一种浅复制.
n*s #s与自身进行n次拼接,它是一种浅复制。
s[2] # c
s[1:6] #bcdef,切片
s[1:6:2] # bdf ,跨步切片,步长为2
len(s) # 11
min(s) #a
max(s) #f
sum(s[,initial]) # 只支持数字
all(s) #True 检查所有项是否为True
any(s) #True 检查任意项是否为True
#还有一个常见的in操作,它不是所有序列都支持。
# in 操作需要注意,序列的迭代对象
if 'a' in s:
pass
注意:只有可变序列支持以下操作.
c=list(s)
s[1]='v' #赋值
s[1:3]='v' #切片赋值,左右两边个数不必相等
s[1:5,2]='cv' #跨步切片赋值,左右两边个数必须相等
del s[i] # 删除
del s[1:3]
del s[1:4:2]
列表的创建方法:
l=[]
l=list()
s='abc'
chars=list(s)
abc
将s转换为一个列表,s必须是一个序列。它是一种浅复制。
chars.append('d')
abcd
追加一个元素
chars.extend('ef')
abcdef
追加一个序列
chars.count('e')
1
计算e出现的次数
chars.index('a')
0
第一个a出现的索引位置
chars.index('a',1)
报错
从第1位开始,第一个a出现的位置
同样的有:
chars.index('a',1,3)
chars.insert(1,a)
aabcdef
在索引1处插入字符
chars.pop([i])
返回元素i并从列表中移除。如果省略i,则从列表尾部开始
chars.remove('a')
搜索x并移除,一次最多移除1个。
chars.reverse()
颠倒
chars.sort([key[,reverse]])
排序,它会修改原列表
s in chars
是否包含
字符串是不可变对象
s='abcdD'
s.capitalize() #大写
'Abcdd'
s.center(2)
'abcdD'
s.center(10)
' abcdD '
s.center(10,'-') #按指定长度居中,并填充指定字符,默认是空格
'--abcdD---'
s.count('a') #计数
1
s.count('d')
1
s.count('d',1) # 从第1位以后开始计数
1
s.count('d',9)
0
s.encode('utf-8') # 按指定编码格式编码
b'abcdD'
s.encode(encoding='gb2312')
b'abcdD'
b'abcd'.decode() # 字节码 解码
'abcd'
s.endswith('D')
True
s.expandtabs()
'abcdD'
'\tabcd'.expandtabs() # 转换tab为空格
' abcd'
s.find('e') #查询某字符或字符串,如果存在返回索引
-1
s.find('D')
4
'abcda'.rfind('a') #从右开始找
4
s.index('e') #查询某字符/字符串的索引,如果不存在会报错,不推荐。
s.rindex('e') #从右开始查找
'abcd{age}'.format(age='e')
'abcde'
'abcd{1}'.format(age='e') #Error
'abcd{0}'.format(age='e') #Error
'abcd{0}'.format('e')
'abcde'
s.isalnum() #全部是字母或数字
True
s.isalpha()
True
s.isdigit()
False
s.islower()
False
s.isupper()
False
s.isspace() #全是空格
False
s.istitle() #姓名格式
False
s.join('d')
'd'
'0'.join('abc') #用s连接指定序列
'a0b0c'
s.rjust(10) #右对齐,左边补齐指定字符
' abcdD'
s.rjust(10,‘-’)
'-----abcdD'
s.ljust(10) #左对齐
'abcdD '
s.rsplit('d') #分隔
['abc', 'D']
s.lstrip() #去掉左边空格
'abcdD'
s.rstrip() #去掉右边空格
'abcdD'
s.strip() #去掉两边的空格
'abcdD'
s.partition('bc') # 将字符串分为三部分:[head,sep,tail],如果不存在sep,则[str,'','']
('a', 'bc', 'dD')
#将包含换行符的字符串变换成行
print('ab\nc'.splitlines())
['ab', 'c']
print('ab\nc'.splitlines(True)) #保留换行符
['ab\n', 'c']
s.swapcase() #变换大小写
'ABCDd'
s.zfill(10) #s.rjust(10,'0')
'00000abcdD'
len(s.ljust(10))
10
s.translate(str.maketrans('abc','321')) #转换表
'321dD'
range([i,] j [,stride])
i的默认值为为0,stride默认值为1.
它有不同点:它不支持所有的切片操作。
不可变列表。元组可以充当对象使用,如(学号,姓名,班级…)作为学生信息对象元组,元组的创建方法(忽略括号,就是元组):
person=(item,) #,是为了语法识别,
a=()
a=item,
b=item0,item1
for a,b,c in persons:
pass
序列有序,映射无序。
dict
是唯一的映射类型。
任何不可变的对象都可以作为字典的键值:字符串,数字,元组。列表、字典包含可变对象不能作为键,元组如果包含可变对象,也不能作为键。
dict
的迭代部分为keys
。
m=dict()
m['a']='a';m[1]='b';m[2]='c'
m
{'a': 'a', 1: 'b', 2: 'c'}
len(m)
3
m[2]
'c'
m[3] # Error 找不到会报错,所以这种索引模式不推荐使用
del m[1] #删除某key ,key不存在会报错
2 in m # 判断2是否是key
True
m.copy() #复制字典
{'a': 'a', 2: 'c'}
m.fromkeys('abc') #创建一个新字典并将序列s中的所有元素作为新字典的键,而且这些键的值均为value
{'a': None, 'b': None, 'c': None}
m.fromkeys('abc','1')
{'a': '1', 'b': '1', 'c': '1'}
m.get('a',1) #查询某key的推荐用法,如果不存在会返回指定默认值
'a'
m.get('c','1')
'1'
m.items() # 返回键值对的序列,注意它不是list,类型是dict_items
dict_items([('a', 'a'), (2, 'c')])
m.items() is list
False
m.pop('e') #弹出指定key的值,如果不存在则Error
m.pop('e','1') #弹出指定的值,如果不存在则返回指定的值 。推荐使用
'1'
m.popitem(2) # 弹出指定的键值队。
(2, 'c')
m.setdefault('e','1') # 查找指定的key对应的value,如果key不存在:m[key]=value,且返回value
m.update({'c':'c'}) # 将b对象的所有键值对加入m中
m.values() # m的值的序列,类型是dict_values
dict_values(['a', 'c', 2, '2', 'c'])
集合是不同元素的无序集合。
集合不提供索引和切片操作。
集合的元素必须是不可变的。
集合分为:set
和 frozenset
(不可变集合)
创建:
s=set('abcd')
fs=frozenset('abcd')
set和frozenset都支持的操作:
len(s)
长度
s.copy()
制作副本
s.difference(t)
差集。s-t,t
是一个序列
s.intersection(t)
交集。s&t
s.isdisjoint(t)
如果s
和t
交集为空,则返回True
s.issubset(t)
s
是否是t
的子集,t
是一个序列
s.issuperset(t)
s
是否是t
的超集,t
是一个序列
s.symmetric_diffenence(t)
s^t
对称差集:(s+t-s^t)
s.union(t)
并集:s+t
可变集合set
支持的操作:
s.add(item)
增加元素。如果item
已经在s中,则无任何效果。
s.clear()
清除所有元素
s.difference_update(t)
删除s
中s&t
的元素
s.discard(item)
删除item
,如果不存在无效果
s.remove(item)
删除item
,如果不存在,KeyError
s.pop()
弹出任意元素。
s.symmetric_difference_update(t)
计算s^t
,结果放入s
中。应该不常用。
s.update(t)
将t
中的元素添加进s中。update
通常都表示加入多个元素
参数
t
均是指可迭代对象(序列)。
可调用类型表示支持函数调用操作的对象:
types.FunctionType
types.MethodType
type
types.BuiltinFunctionType
先看一例:
def foo(x,y):
"return x+y"
return x+y
bar=lambda x,y:x+y
foo.z=1
foo.x=2
z=foo(1,2)
print(z)
print(foo.z)
print(foo.__dict__)
# 输出
3
1
{'z': 1, 'x': 2}
请注意foo.z=1
之所以能这么使用,是因为foo
也是一个对象,它包含一个dict
属性。
用户自定义的函数f
包含以下属性:
属性 | 描述 |
---|---|
f.__doc__ |
文档字符串 |
f.__name__ |
函数名字 |
f.__dict__ |
包含函数属性的字典 |
f.__code__ | 编译的字节码 |
f.__defaults__ | 包含默认参数的元祖 |
f.__globals__ | 定义全局命名空间的字典 |
f.__closure__ | 包含与潜逃作用域相关数据的元祖 |
方法与用户自定义函数
的区别是,它在这里指类中定义的函数。
有三种常见的方法:
class Foo(object):
def instance_method(self,arg): #实例方法
pass
@classmethod
def class_method(cls,arg): # 类方法,不需要实例
print(cls)
pass
@staticmethod
def static_method(arg): #静态方法,不需要实例
pass
Foo.class_method(1) # 输出
Foo.instance_method(2)# Error
实例方法默认将一个实例作为第一参数传入(self)
.
类方法默认将类本身当作对象,并将该对象作为一个第一个参数。
静态方法它不能将self传入。
看一例子:
class Foo(object):
def sum(self,x,y):
return x+y
f=Foo()
s=f.sum
print(s(1,2))
>> 3
print(s(1,2))
f.sum
与f.sum(1,2)
的区别是:这是两种不同的操作,前一个的.
表示对象属性查找,第二个.
表示函数调用。调用方法时两种操作都会发生。
上述s=f.sum
是将一个实例和一个方法绑定到了s
上,故s
又称绑定方法
,s
封装了一个实例和一个方法。显然创建绑定方法时实例不是必须的,只需要传入类和方法,然后在调用时传入一个实例即可。它同java的反射机制很类似。
types.MethodType
的属性如下:
属性 | 描述 |
---|---|
m.__doc__ | 文档字符串 |
m.__name__ | 文档字符串 |
m.__class__ | 该方法所在的类 |
m.__func__ | 方法对象 |
m.__self__ | 实例 |
m.__dict__ |
字典 |
对象types.BuiltinFunctionType
用于表示用C/C++
实现的函数和方法。
内置方法只有三个属性:
属性 | 描述 |
---|---|
m.__doc__ | 文档字符串 |
m.__name__ | 文档字符串 |
m.__self__ | 实例 |
对于len(seq)
这样的内置函数,self
被设置为None
,表明函数没有绑定到任何特定的对象。
x.append('a')
中self被设置为x
。
类对象和实例也可以当作可调用的对象进行操作。
定义类时通常会生成一个type
类型对象,使用type(instance)
可以查看。
它有以下属性:
属性 | 描述 |
---|---|
t.__doc__ | 文档字符串 |
t.__name__ | 类名称 |
t.__bases__ | 由基类构成的元祖 |
t.__dict__ | 保存类方法和变量的字典 |
t.__module__ | 定义类的模块名称 |
t.__abstractmethods__ | 抽象方法名称的集合 |
模块
类型是一个容器,可保存使用import
语句加载的对象。
import foo
foo.x=1
foo.sum(1,2)
显然模块最重要的属性是一个dict
,它保存了foo
相关的信息,用于调用时查找。
模块的dict结构如下:
{
'foo':dict,
'foo0':dict
}
它以import
的类名/包名作为key
(命名空间)。
有以下一些种类:
yield
)Ellipsis
):暂时不懂。生成器对象有两个用途:迭代器,容器。它有如下主要属性:
属性 | 描述 |
---|---|
g.__next__ | 执行函数,直到遇到下一条yield语句为止,并返回该值 |
g.send(value) | 向生成器发送一个值,yield语句会返回该值,生产器将一直执行直到下一次yield |
g.close() | |
g.gi_running | 显示生成器函数目前是否正在运行的整数 |
切片对象是在a[1:3:1]
时创建的。它有三个只读属性:
start
stop
step
举一例:
s=slice(10,20)
print(s.indices(100)) #如果序列长100,则该切片如何应用,返回值是(start,stop,step)
print(s.indices(2)) #如果序列长2,则该切片如何应用
print(s.indices(20))
print(s.indices(10))
print('abcdefghijklmn'[s]) #应用切片
# 输出
(10, 20, 1)
(2, 2, 1)
(10, 20, 1)
(10, 10, 1)
klmn
对象实现了某些方法,就可以支持Python内置函数的一些功能。
如len(obj)
可用的要求是obj
实现了__len__(self)
方法。
涉及到三个方法:
__new__(cls [,*args [,**kwargs] ])
__init__(self[,*args [,**kwargs] ])
后
调用。__del__(self)
del x
,是减少引用计数,不会立刻调用此方法。*args
如果我们不确定要往函数中传入多少个参数,或者我们想往函数中以列表和元组的形式传参数时,那就使要用args
;
**kwargs
如果我们不知道要往函数中传入多少个关键词参数,或者想传入字典的值作为关键词参数时,那就要使用**kwargs
,其中kwargs
是一个dict
。
class Foo(object):
def add(self,x,y,**kwargs):
print(kwargs)
return x+y
m=dict()
m['a']=3
print(Foo().add(3,4,s=5,t=6))
>>>
{'s': 5, 't': 6}
7
创建一个对象的各种字符串表示。它涉及:
__format__(self,format_spec)
__repr__(self)
__str__(self)
以上每个方法都可以利用一个Python内置函数访问,这也是Python常见做法。
看一例子:
class FooClass(object):
def __init__(self):
print("__init__")
def __repr__(self):
return "FooClass repr"
def __str__(self):
return "FooClass str"
def __format__(self, format_spec):
return "FooClass is {0}".format(format_spec)
fc=FooClass()
print(fc)
print(str(fc))
print(repr(fc))
print(format(fc,"fc"))
>>
FooClass str # str()函数,如果__str__为定义则调用repr
FooClass repr # repr函数
FooClass is fc # format()函数
这是与Java的不同点,Java实现类似的功能,采取的方法是定义一个接口,而类实现这个接口。Python直接定义一个内置方法,这说明Python的参数校验比Java松散许多,带来的后果是Python程序较为精简,因为它只需要写出实接口的方法即可,而不需要声明实现了某个接口。
涉及以下方法:
方法 | 描述 |
---|---|
__bool__(self) |
关系到if x: 的结果 |
__hash__(self) |
hash(x) 的结果,理论上==的对象hash应该相等 |
__lt__(self,other) |
返回self |
__le__(self,other) |
返回self<=other 的真假 |
__gt__(self,other) |
返回self>other 的真假 |
__ge__(self,other) |
返回self>=other 的真假 |
__eq__(self,other) |
返回self==other 的真假 |
__ne__(self,other) |
返回self!=other 的真假 |
如果要使用==
应定义__eq__
;
如果要使用对象排序或者max
等,必须至少定义__lt__
.
应用于定义抽象的基类和接口,它涉及:
__instancecheck__(cls,object)
: isinstance(object,cls)
__subclasscheck_-(cls,sub)
: issubclass(sub,cls)
属性访问即(.)
,del
,对象的读写删除属性。
对象的属性访问相关的方法如下:
方法 | 描述 |
---|---|
__getattribute__(self,name) |
返回self.name |
__getattr__(self,name) |
返回self.name |
__setattr__(self,name,value) |
self.name=value |
__delattr__(self,name) |
删除self.name |
注意访问属性时,顺序如下:
__getattribute__
__getattr__
它同属性访问有相同的功能,不过它使用一个额外逻辑的描述符对象包装属性,往往用于实现对象系统的底层功能(绑定和非绑定方法,类方法,静态方法和特性),极少情况才需要定义.
方法 | 描述 |
---|---|
__get__(self,name) |
返回self.name |
__set__(self,name,value) |
self.name=value |
__delete__(self,name) |
删除self.name |
要使对象支持映射的方法,相关方法如下:
方法 | 描述 |
---|---|
__len__(self) |
返回self 长度,关联len(x) 结果 |
__getitem__(self,key) |
关联self[key] |
__setitem__(self,key,value) |
self[key]=value |
__delitem__(self,key) |
删除self[key] |
__contains__(self,obj) |
关系到if obj in x 的结果 |
请注意__getitem__
同属性访问的区别是,它使用的是索引,而不是属性查找。
class Foo(object):
def __getitem__(self, item):
print("do getitem")
return "__getitem__"
def __setitem__(self, key, value):
print("__setitem__")
pass
f=Foo()
f['a']=3
print(f['a'])
>>>
__setitem__
do getitem
__getitem__
如果对象obj
要支持迭代,它必须提供方法__iter__()
,返回一个迭代器对象。而迭代器对象必须实现一个方法:__next__()
返回下一个对象或在迭代结束时返回StopIterationError
。
Python实现数学操作的原理:
执行表达式:x + y
:
x.__add__(y)
x
没有add
,则解释为y.__radd__(x)
要某类对象支持数学操作,就需要在类中实现对应的方法。
对象通过提供__call(self[ , *args [, **kwargs]])
方法可以模拟函数的行为,X(arg1,arg2,...)
等同于调用X.__call__(self,arg1,arg2,...)
.它可用于创建仿函数或代理。
class DistanceFrom(object):
def __init__(self,origin):
self.origin=origin
def __call__(self,x):
return abs(x-self.origin)
nums=[1,37,42,101,13,9,-20]
nums.sort(key=DistanceFrom(10))
print(nums)
>>
[9, 13, 1, 37, -20, 42, 101]
sort(key=None,reverse=False)
:其中key
接收一个带有一个参数的函数,在运行排序时将元素值当作参数传入该函数,得到的结果即为提取的排序因子。
所以以上函数还可以这样改造:
class DistanceFrom(object):
def __init__(self, origin):
self.origin = origin
@staticmethod
def distance(x):
return abs(x-10)
nums = [1, 37, 42, 101, 13, 9, -20]
nums.sort(key=DistanceFrom.distance)
print(nums)
语法:
with context_obj [as var]:
statements
其中context_obj
需要实现__enter__
和__exit__
两个方法。
class DistanceFrom(object):
def __enter__(self):
print('enter')
return "1"
def __exit__(self, exception_type, exception_val, exception_stack):
print("exit")
pass
df=DistanceFrom(0)
with df as var:
print("statements")
print(var)
>>
enter
statements
exit
1
dir(obj)
函数通常用于检查对象,查看对象可使用信息。要支持该调用,类需要实现__dir__(self)
方法。