定义与创建

按照 Python 的编程习惯,类名以大写字母开头,紧接着是(object),表示该类是从哪个类继承下来的。
由于Python是动态语言,对每一个实例,都可以直接给他们的属性赋值,例如,给xiaoming这个实例加上name、gender和birth属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Person(object):
pass
p1 = Person()
p1.name = 'Bart'
p2 = Person()
p2.name = 'Adam'
p3 = Person()
p3.name = 'Lisa'
L1 = [p1, p2, p3]
L2 = sorted(L1,lambda p1,p2 : cmp(p1.name,p2.name))
print L2[0].name
print L2[1].name
print L2[2].name

初始化

init()方法,当创建实例时,init()方法被自动调用,我们就能在此为每个实例都统一加上属性
init() 方法的第一个参数必须是 self(也可以用别的名字,但建议使用习惯用法),后续参数则可以自由指定,和定义函数没有任何区别。

1
2
3
4
5
6
7
8
9
10
11
12
class Person(object):
def __init__(self,name,sex,birth,**kw):
self.name = name
self.sex=sex
self.birth=birth
for k, v in kw.iteritems():
setattr(self, k, v)
xiaoming = Person('Xiao Ming', 'Male', '1990-1-1', job='Student')
print xiaoming.name
print xiaoming.job

私有变量

如果一个属性由双下划线开头(__),该属性就无法被外部访问。但是,如果一个属性以”__xxx__“的形式定义,那它又可以被外部访问了,以”__xxx__“定义的属性在Python的类中被称为特殊属性,有很多预定义的特殊属性可以使用,通常我们不要把普通属性用”__xxx__“定义。
以单下划线开头的属性”_xxx”虽然也可以被外部访问,但是,按照习惯,他们不应该被外部访问。

1
2
3
4
5
6
7
8
9
class Person(object):
def __init__(self, name):
self.name = name
self._title = 'Mr'
self.__job = 'Student'
p = Person('Bob')
print p.name
print p._title
print p.__job

静态变量

静态变量只属于类的属性,由于Python是动态语言,类属性也是可以动态添加和修改的.
当实例属性和类属性重名时,实例属性优先级高,它将屏蔽掉对类属性的访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Person(object):
count = 0
def __init__(self,name):
self.name = name
Person.count = Person.count + 1
p1 = Person('Bob')
print Person.count
p2 = Person('Alice')
print Person.count
p3 = Person('Tim')
print Person.count
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Person(object):
address = 'Earth'
def __init__(self, name):
self.name = name
p1 = Person('Bob')
p2 = Person('Alice')
print 'Person.address = ' + Person.address
p1.address = 'China'
print 'p1.address = ' + p1.address
print 'Person.address = ' + Person.address
print 'p2.address = ' + p2.address
del p1.address
print p1.address

定义方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Person(object):
def __init__(self, name, score):
self.name = name
self.__score = score
def get_grade(self):
if self.__score<60:
return 'C'
elif self.__score<90:
return 'B'
else:
return 'A'
p1 = Person('Bob', 90)
p2 = Person('Alice', 65)
p3 = Person('Tim', 48)
print p1.get_grade()
print p2.get_grade()
print p3.get_grade()

因为方法也是一个属性,所以,它也可以动态地添加到实例上,只是需要用 types.MethodType() 把一个函数变为一个方法:

1
2
3
4
5
6
7
8
9
10
11
class Person(object):
def __init__(self, name, score):
self.name = name
self.score = score
def get_grade(self):
return 'A'
p1 = Person('Bob', 90)
print p1.get_grade
print p1.get_grade()

类的方法

通过标记一个 @classmethod,该方法将绑定到 Person 类上,而非类的实例。类方法的第一个参数将传入类本身,通常将参数名命名为 cls,上面的 cls.count 实际上相当于 Person.count。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Person(object):
__count = 0
def __init__(self,name):
self.name = name
Person.__count = Person.__count + 1
@classmethod
def how_many(cls):
return cls.__count
print Person.how_many()
p1 = Person('Bob')
print Person.how_many()

类的继承

继承的初始化

1
2
3
4
5
6
7
8
9
class Person(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender
class Student(Person):
def __init__(self, name, gender, score):
super(Student, self).__init__(name, gender)
self.score = score

一定要用 super(Student, self).__init__(name, gender) 去初始化父类,否则,继承自 Person 的 Student 将没有 name 和 gender。
函数super(Student, self)将返回当前类继承的父类,即 Person ,然后调用__init__()方法,注意self参数已在super()中传入,在__init__()中将隐式传递,不需要写出(也不能写)。

判断数据类型

函数isinstance()可以判断一个变量的类型,既可以用在Python内置的数据类型如str、list、dict,也可以用在我们自定义的类,它们本质上都是数据类型.isinstance(s, Person)
Student和Teacher继承Person,a,b,c分别是Person,Student,Teacher的实例,a是person类,不是student或Teach,但是b即时Person又是Student。

多态

由于Python是动态语言,所以,传递给函数的参数 x 不一定是 子类 或 父类类型。任何数据类型的实例都可以,只要它有一个相同参数的方法即可:
这是动态语言和静态语言(例如Java)最大的差别之一。动态语言调用实例方法,不检查类型,只要方法存在,参数正确,就可以调用。

多继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class A(object):
def __init__(self, a):
print 'init A...'
self.a = a
class B(A):
def __init__(self, a):
super(B, self).__init__(a)
print 'init B...'
class C(A):
def __init__(self, a):
super(C, self).__init__(a)
print 'init C...'
class D(B, C):
def __init__(self, a):
super(D, self).__init__(a)
print 'init D...'

获取对象信息

typetype(123)
dir()dir(123)
只显示自己的属性

1
2
3
4
5
def only_my(s):
if(s[0]!='_'):
return s;
print filter(only_my, dir(s))

dir()返回的属性是字符串列表,如果已知一个属性名称,要获取或者设置对象的属性,就需要用 getattr() 和 setattr( )函数了:

1
2
3
4
getattr(s, 'name') # 获取name属性
setattr(s, 'name', 'Adam') # 设置新的name属性
getattr(s, 'age') # 获取age属性,但是属性不存在,报错:
getattr(s, 'age', 20) # 获取age属性,如果属性不存在,就返回默认值20:

定制类

特殊方法

__str__和__repr__

1
2
3
4
5
6
7
8
9
10
11
class Person(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender
def __str__(self):
return '(Person: %s, %s)' % (self.name, self.gender)
__repr__ = __str__
p = Person('Bob', 'male')
print p
p

__cmp__

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
def __str__(self):
return '(%s: %s)' % (self.name, self.score)
__repr__ = __str__
def __cmp__(self, s):
if self.name < s.name:
return -1
elif self.name > s.name:
return 1
else:
return 0

Student 类实现了cmp()方法,cmp用实例自身self和传入的实例 s 进行比较,如果 self 应该排在前面,就返回 -1,如果 s 应该排在前面,就返回1,如果两者相当,返回 0。

__len__

1
2
3
4
5
6
7
class Students(object):
def __init__(self, *args):
self.names = args
def __len__(self):
return len(self.names)
ss = Students('Bob', 'Alice', 'Tim')
print len(ss)

四则运算与类型转化

实现分数的四则运算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
def gcd(a,b):
if b==0 :
return a
else:
return gcd(b,a%b)
class Rational(object):
def __init__(self, p, q):
self.p = p
self.q = q
def __add__(self, r):
tmp=Rational(self.p * r.q + self.q * r.p, self.q * r.q)
tmp.reduces()
return tmp
def __sub__(self, r):
tmp=Rational(self.p*r.q-self.q*r.p , self.q*r.q)
tmp.reduces()
return tmp
def __mul__(self, r):
tmp=Rational(self.p*r.p,self.q*r.q)
tmp.reduces()
return tmp
def __div__(self, r):
tmp=Rational(self.p*r.q,self.q*r.p)
tmp.reduces()
return tmp
def __str__(self):
return '%s/%s'%(self.p,self.q)
__repr__ = __str__
def reduces(self):
c = gcd(abs(self.p),abs(self.q))
self.p=self.p/c
self.q=self.q/c
def __int__(self):
return self.p // self.q
def __float__(self):
return float(self.p)/self.q
r1 = Rational(1, 2)
r2 = Rational(1, 4)
print r1 + r2
print r1 - r2
print r1 * r2
print r1 / r2
print int(Rational(7,3))
print float(Rational(5,2))

@property

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Student(object):
def __init__(self, name, score):
self.name = name
self.__score = score
@property
def score(self):
return self.__score
@score.setter
def score(self, score):
if score < 0 or score > 100:
raise ValueError('invalid score')
self.__score = score
@property
def grade(self):
if self.score < 60:
return 'C'
if self.score < 80:
return 'B'
return 'A'
s = Student('Bob', 59)
print s.grade
s.score = 60
print s.grade
s.score = 99
print s.grade

__slots__

如果要限制添加的属性,例如,Student类只允许添加 name、gender和score 这3个属性,就可以利用Python的一个特殊的slots来实现。

1
2
3
4
5
6
class Student(object):
__slots__ = ('name', 'gender', 'score')
def __init__(self, name, gender, score):
self.name = name
self.gender = gender
self.score = score

__call__

一个类实例也可以变成一个可调用对象,只需要实现一个特殊方法call()。

1
2
3
4
5
6
7
8
9
10
class Person(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender
def __call__(self, friend):
print 'My name is %s...' % self.name
print 'My friend is %s...' % friend
p = Person('Bob', 'male')
p('Tim')