异常
除了关键字不一样外,其余的与java完全相同。
try-except-finally分别表示java中的try,catch,finally。java中的throw在python中用raise代替。
try:
f = file('x.txt')
except Exception, e: # 捕获异常,并将异常信息用e变量存储
print e
else: # 程序没有异常时执行
print 'no except'
finally: # 与java中一样
print 'finally'
python中所有的异常都是Exception的子类。else与finally都存在时,如果程序出现异常,不执行else但执行finally;如果程序没有异常,则先执行else,再执行finally。
raise
raise如果不带参数的话,会将当前的异常原样抛出。
logging
日志模块。
basicConfig():
类
python中所有的数据都是对象,包括基本数据类型,而赋值就是将对象的地址赋值给对应的变量,使变量与对象关联起来。由于基本数据类型也是对象,因此a=1语句会执行两个操作:分配一块内存存储1,并使用变量a记录该内存的地址。
类的定义与java一样,通过class关键字表示。
函数
所有函数至少有一个参数,并且所有函数的第一个参数都是self(它代表当前对象自身,同java中的this一样),在使用函数时不需要为第一个参数传值。
所有类的构造函数名都是__init__,而且第一个参数也必须是self。可以在构造函数中为类定义一些全局变量。
类的实例化也与java类似,只不过不需要关键字new。如:
class Demo:
def __init__(self,a): # 构造函数
self.a = a # 定义一个全局变量a
def function(self,b):
return 'Demo%s-%s'%(self.a,b)
d = Demo(3) # 实例化对象,并且传入构造函数需要的参数
print d.function(5) # Demo3-5
python允许对实例变量绑定任何数据,也就是说获取对象后,可以为对象动态添加属性,新添加的属性只为该实例所有,别的实例无法使用。如下:d = Demo(3)
d1 = Demo(3)
d.name = 'name'
print d.name # name
print d1.name # AttributeError
其实这和__init__一样,在__init__中self指对象本身,而此时也没有任何属性,都是在__init__函数中新添加的属性,只不过__init__是构造函数,每一个对象都会执行,也就保证了每一个对象都会有在__init__中定义的属性。
继承
定义类时,可以在类名后面跟(),并在()中写上父类。所有的类都继承于object。如
class Demo:
def function(self):
return 'Demo'
class Demo2(Demo):# Demo2继承于Demo
a = None
d = Demo2()
print d.function() # Demo
一个类可以多继承,只需要在()中用逗号将父类隔开即要。
class P1(object):
def say(self):
print("say")
class P2(object):
def sing(self):
print("sing")
class C(P1, P2): # C继承于P1,P2
pass
c = C()
c.say()
c.sing()
私有
以双下划线开头的变量是私有的,外界无法直接访问。但要注意以双下划线开头,并且以双下划线结尾的是特殊变量,是可以直接访问的。
class Demo:
def __init__(self,a):
self.__a = a # 私有属性__a
self.a = a
def function(self,b):
return 'Demo%s-%s'%(self.__a,b) # 私有变量,类内部可以访问
def __private(self,c): # 私有方法
print "__private",c
d = Demo(3)
print d.a # 3
print d.function(5) # Demo3-5
d.__private(10) # AttributeError: Demo instance has no attribute '__private'
print d.__a # AttributeError: Demo instance has no attribute '__a'
protected
以_开头的变量是protected的。本类及其子类才可访问。
属性
分为类属性与实例属性。实例属性指的是为某一个实例绑定的属性,它只属于该实例。而类属性是类中定义的属性——所有实例共享的,也可以直接通过类名点调用的。
当类属性与实例属性同名时,实例属性会重写类属性。
class Demo(object):
name = 'name' # 类属性
def __init__(self):
self.age = 15 # 实例属性
d = Demo()
d2 = Demo()
d.score = 84 # 实例属性,该属性只属于d,d2是没有的
print(d.age, d.name, d.score) # 15 name 84
print(d2.name, d2.age) # name 15——d2是没有score属性的
d2.name = 'd2name' # d2定义了实例属性,会覆盖掉Demo的类属性
print(d2.age, d2.name) # 15 d2name
print(d.age, d.name, d.score) # 15 name 84
print(Demo.name) # name
del d2.name # 删除d2的name属性
print(d2.name) # name d2的name属性就变成了继承于Demo的类属性
从上面可以看出:1,实例属性只属于当前实例,跟别的实例没关系,跟类也没关系;2,类属性属于所有实例,也可直接通过类名点调用;3,实例属性可以覆盖掉类属性,所以实例属性不能与类属性重名。绑定方法
直接通过实例点的方法可以为实例绑定属性,也可以为实例绑定某个方法,但需要借助types.MethodType。如下:
from types import MethodType
class Demo(object):
name = 'name' # 类属性
def __init__(self):
self.age = 15 # 实例属性
def say(self,age):
print('ags = %d' % age)
d = Demo()
d.say = MethodType(say, d) # 为d实例绑定方法
d.say(11) # ags = 11
要注意:类的方法的第一个参数一定是self,所以绑定到实例中的方法第一个参数也一定是self。
另外,为实例绑定的方法也与绑定的属性一样,只是属于当前这个实例,别的实例是没有办法调用的。如果想所有的实例都能调用,就将该方法写成类方法。
@property
将一个类方法变成一个只读属性,调用时通过类名点——后不需要加(),也不能为该属性重新赋值——除非定义了setter方法。如:
class Demo(object):
@property
def test(self):
print('-----')
return 1
d = Demo()
print(d.test) # 转变为属性,所以直接通过类名点方法调用
try:
d.test = '2' #将test方法转变为一个只读属性,所以不能赋值
except Exception as e:
print(e) # can't set attribute
@property实质上生成了一个与方法名同名的属性——property是一个装饰器。同样,也可为该属性定义setter方法:
class Demo(object):
@property
def test(self):
print('getter')
@test.setter
def test(self,value):
print('value = %s'%value)
d = Demo()
d.test # getter
d.test = 'aaaa' # value = aaaa
首先通过@properyt生成了一个名为test的属性,而@property本身又创建了另一个test.setter装饰器,可以通过@test.setter为test属性绑定了setter方法。
简单点理解就是:@property相当于同名属性的getter方法,使用类名点的时候就会调用该getter方法;而@xxx.setter相当于为xxx属性定义了setter方法,每一次为该属性赋值时就会走该方法。
注意:@property与@xxx.setter装饰的方法名必须一样,其中的xxx指的是就方法名。有@property时,可以没有@xxx.setter,但必须先定义了@property才能定义@xxx.setter。
内置属性和方法
__init__:构造函数。
__len__:类的长度,定义该方法的类可以通过len()函数求长——len()函数的实质上也是调用__len__函数。
__str__:java中toString(),定制直接print()实例时输出的语句。
__repr__:直接输出对象时输出语句。__str__定义的是通过print(实例)输出的内容,__repr__是直接运行对象时输出的内容。一般情况下,两者一样。如下:
>>> class Demo(object):
... def __repr__(self):
... return 'hahahaha'
...
>>> Demo()
hahahaha
__slots__:限定可以绑定的实例属性(注:方法也是属性,且类属性不受__slots__限制)。一般情况下,可以为实例绑定任意的属性,但定义__slots__后,能绑定的属性就被限制死了。如:
class Demo(object):
__slots__ = ('age', 'name', 'say') # __slots__是一个类属性,它是一个元组
def __init__(self):
self.age = 15 # 实例属性,age定义在__slots__中,
def yes(self): # 类属性,不受__slots__限制
print("yes")
def say(self,age):
print('ags = %d' % age)
d = Demo()
d.say = MethodType(say, d) # __slots__中定义了say属性,所以可以绑定
d.say(11) # ags = 11
try:
d.score = '1111' # _slots__中没有定义score,所以该句会报错
except AttributeError as msg:
print(msg) # 'Demo' object has no attribute 'score'
还要注意的是:
1,__slots__中不能含有类属性。因为实例属性不能与类属性重名。
2,__slots__只对当前类起作用,对其子类不起作用,除非子类也定义了__slots__。如果子类定义了__slots__,那么子类的__slots__就是自身的__slots__加上父类的__slots__。
__iter__:返回一个iterator。将一个对象用于for循环时,循环的其实就是__iter__的返回值。如:
class P1(object):
name = ('a', 'b', 'c', 'yield ')
def __init__(self):
self.n = 0
def __iter__(self):
return (x*x for x in range(11))
for x in P1(): # __iter__返回的是一个iterator,此时的循环已经跟P1()这个对象没关系了
print(x) # 输出0到10的平方
一般来说,它返回的都是self,表示该类本身就是一个iterator,那么这个时候该类就需要重写下面的__next__方法。
__next__:每一个iterator通过反复调用迭代器的__next__()方法来返回后继值。也就是说,每一个iterator都需要重写该方法,并且在它的__next__中返回后继值。如:
class P1(object):
name = ('a', 'b', 'c', 'yield ')
def __init__(self):
self.n = 0
def __iter__(self):
return self # 该对象本身就是iterator
def __next__(self):
if self.n >= len(self.name):
return 'end-----'
result = self.name[self.n]
self.n += 1
return result
for x in P1(): # <span style="font-family: Arial, Helvetica, sans-serif;">该对象本身就是iterator,所以可以用于for循环
</span> print(x)
每一次循环时都会从__next__中取值。在本例中的循环是一个死循环。
__getitem__:使对象可以通过下标获取数据。如:
class P1(object):
name = ('a', 'b', 'c', 'yield ')
def say(self):
print("say")
def __getitem__(self, item):
return self.name[item]
p = P1()
print(p[2]) # c 其执行的是__getitem__方法,并将2传入item
对于切片来说,执行切片时走的也是__getitem__方法,处理方式如下: def __getitem__(self, item):
if isinstance(item, int): # 是下标时处理
return self.name[item]
if isinstance(item, slice): # 判断是否是切片
start = item.start
end = item.stop
# 通过start,end等属性截取相应的元组返回即可
与之类似的还有__setitem__与__delitem__,一个是设置一个是删除。
__getattr__:当调用不存在的属性时,python会调用对象的__getattr__去动态地获取属性值——包括通过实例点以及getattr()方法获取。如果调用的是已经存在的属性,就不会执行__getattr__。如:
class Demo(object):
def __getattr__(self,attr):
if attr == 'age':
return 20
d = Demo()
print(d.age) # 20 age属性不存在,所以调用__getattr__方法获取,并将attr赋值为age字符串
print(d.name) # None
对于d对象来说,age即不是对象属性也不是类属性,所以d.age时会执行__getattr__方法,同时将age传入__getattr__的attr参数中。
对于d.name,由于__getattr__在attr为name时并没有返回值,所以默认的返回None。
对于一个函数来说,它可以返回一个函数。所以__getattr__也可以返回一个函数或者一个lambda表达式。如下:
class Demo(object):
def __getattr__(self,attr):
if attr == 'say':
return say
def say():
print('hello')
d = Demo()
print(type(d.say)) # <type 'function'><type function="">
d.say() # 在attr为say时,返回的是一个函数,使用()执行该函数</type>
在d.say时,会调用__getattr__,同时将attr赋值为say,此时__getattr__返回的是一个函数,所以d.say指向的是一个函数,也就是倒数第二句输出的类型为function。再通过()执行该函数,就相当于直接执行say函数。
__call__:直接将实例当方法调用时,会走该方法。如:
class Demo(object):
def __call__(self, *args, **kwargs):
return '__call__调用'
print(Demo()()) # __call__调用
Demo()返回一个Demo实例,后面的()实例当方法调用,就相当于调用实例的__call__方法,故Demo()()返回的是__call__的返回值。
结合__getattr__可以写出很有意思的效果:
class P1(object):
def __init__(self, name = ''):
self.name = name
def __getattr__(self, item):
if item == 'age' or item == 'sex':
return P1("%s/%s" % (self.name,item))
if item == 'say':
return self
def __call__(self, *args, **kwargs):
return P1("%s/%s" % (self.name, args[0]))
def __str__(self):
return self.name
__repr__ = __str__
print(P1().sex.say('xxxxx').age) # /sex/xxxxx/age
首先P1(),会创建一个P1对象,该对象的name属性为''“,__init__方法中指定的。
其次,获取sex属性,因为没有定义sex属性所以执行__getattr__方法,返回的就是另一个P1对象,它的name值是'/sex'
再获取say,直接返回上一个P1对象,然后执行该对象,所以执行该对象的__call__方法,该方法返回的是P1对象,其name为/sex/xxxxx
最后获取age属性,返回一个name为/sex/xxxxx/age的P1对象。
使用print()方法输出对象,会调用对象的__str__方法,所以输出的就是对象的name属性。即:/sex/xxxxx/age。
枚举
方法一
继承enum模块中的Enum即可。如下:
import enum
@enum.unique # 保证各个枚举变量值不相同
class Demo(enum.Enum):
MAN = 0 # MAN值被设置为1
WOMEN = 3
MIDDLE = 'renyao'
print(Demo.MAN) # Demo.MAN
print(Demo['MAN']) # Demo.MAN
print(Demo(3)) # Demo.WOMEN 这里的3是变量的值,不是下标
try:
print(Demo(2)) # 这句会报错
except Exception as e:
print(e) # 2 is not a valid Demo
print(Demo('renyao')) # Demo.MIDDLE 因为有变量值为renyao,所以可以取出来。
取值方式一共有三种:类名点,按字典形式取,以及通过变量值取。
取对应变量的值,是在变量点value。如:Demo.MAN.value返回的是0。
方法二
通过enum模块中Enum类创建一个枚举类。如:
d = enum.Enum('D', ('month', 'year', 'day'))
print(d(3)) # D.day
print(d.month) # D.month
print(d['year']) # D.year
第一个参数相当于枚举类的类名,后面元组中是各个常量。d为枚举类的对象。
与java不同的地方在于:枚举类的值从1开始。如:
import enum
d = enum.Enum('D', ('month', 'year', 'day'))
for name, member in d.__members__.items():
print(name, member, member.value) # month D.month 1
循环时,取value值是从1开始的。