Python学习笔记–5.面向对象编程
MrZigZag @ Peking University
2015-07-17
1.定义
通过class
关键字定义类,父类(object)
。
class MyClass(object):
pass
没有合适的继承类时可以使用object
类,这是所有的类都会继承的类。
自由绑定,Python是动态语言,可以在运行过程中实时地给实例instance
绑定新的属性,同时还可以绑定新的方法。
>>> ins1 = MyClass()
>>> ins1.name = 'Test'
def set_age(self, age):
self.age = age
from types inport MethodType
s = Student()
s.set_age = MethodType(set_age, s, Student)
方法,类的初始化方法是__init__(self, args)
,任何方法第一个参数都是self
,但是使用时不需要传进去。
2.访问限制
属性名称加上__
表示private,__prop
会被解释器解释成_Classname__prop
,要访问__prop
可以写__Classname__prop
。
双下划线开头双下划线结尾的属性属于特殊变量,可以访问,一般不能使用这个命名方法。
单下划线开头的变量可以访问,但是一般被视为private变量。
3.继承和多态
继承之后,子类的方法会覆盖父类的方法。
多态保证了只需要知道父类,并不需要确切的子类就可以放心的使用父类中的方法。因而调用方只管调用不管细节。符合“开闭原则”。对扩展开放,对修改封闭
。
class Animal(object):
def run(self):
print 'Animal is running!'
class Dog(Animal):
def run(self):
print 'Dog is runnning!'
def run_twice(animal):
animal.run()
animal.run()
>>> run_twice(Animal())
Animal is running!
Animal is running!
>>> run_twice(Dog())
Dog is running!
Dog is running!
根据多态的性质,任何时候最好都要使用继承,如果没有合适的父类则直接继承object
。
4.获取对象信息
拿到一个对象的引用时,需要确定对象的类型和包含的方法。可以使用多种方法。
使用type(),基本类型都可以使用type()
函数判断。
>>> type(1)
<type 'int'>
>>> type('hello')
<type 'str'>
>>> type(None)
<type 'NoneType'>
>>> type(abs)
<type 'builtin_function or method'>
>>> type(Animal())
<type '__main__.Animal'>
type()
函数返回type
类型,Python把每种type类型都定义好了常量,放在types
模块中。
>>> import types
>>> type('abc') == types.StringType
True
>>> type(u'abc') == types.UnicodeType
True
>>> type([]) == types.ListType
True
>>> type(int) = types.TypeType
不同类型都有对应的type,所有类型本身的类型就是一种TypeType
。
使用isinstance(),判断class的类型使用type()并不是很方便,直接使用isinstance()
函数。
>>> a = Ainimal()
>>> d = Dog()
>>> isinstance(d, Dog)
True
>>> isinstance(d, Animal)
True
可见isinstance()对继承关系也是有用的。当然,isinstance()还可以判断变量是某些类型中之一。
>>> isinstance('a', str)
True
>>> isinstance('a', (str, unicode))
True
使用dir()可以获得一个对象的所有属性和方法。返回一个包含字符串的list。
同时配合getattr()
、setattr()
、hassttr()
可以直接操作一个对象的状态。
>>> hasattr(obj, 'x')
True
>>> hasattr(obj, 'y')
False
>>> setattr(obj, 'y', 10)
>>> getattr(obj, 'y')
10
>>> obj.y
10
>>> getattr(obj, 'z', 404)
404
一般正确的用法:
def readImage(fp):
if hasattr(fp, 'read'):
return readData(fp)
return None
5.slots
基于Python的动态语言特性,创建class实例之后我们还可以在运行过程中给该实例绑定新的属性和方法,但是新绑定的属性和方法对其他实例是不起作用的。为了给所有实例都绑定方法,可以直接给class绑定新的方法。
def set_score(self, score):
self.score = score;
Student.set_score = Method_Type(set_score, None, Student)
使用slots可以限制class的属性。Python允许在定义class的时候,定义一个特殊的__slots__
变量,来限制该class能添加的属性。__slots__
仅仅对当前class有效,对继承的子类是不起作用的。
class Student(object):
__slots__ = ('name', 'age') # Use tuple to describe __slots__
如果在子类中定义__slots__
,那么子类允许定义的属性就是自身的__slots__
加上父类的__slots__
。
6.@property
绑定属性时,为了方便起见,有时候会将属性直接暴露出去,但是这样就没法检查参数,导致属性可以随便改。因此,我们需要限制属性的范围。
class Student(object):
def get_score(self):
return self._score
def set_score(self, val):
if not isinstance(val, int):
raise ValueError('score must be an integer')
if val < 0 or val > 100:
raise ValueError('score must between 0 - 100')
self._score = val
但是这样做就没法将属性直接暴露出去方便读写了。因此我们可以用装饰器decorator给函数动态加上功能。Python内置的@property
装饰器就是负责把一个方法变成属性调用。把一个getter方法变成属性只需加上@property
,此时@property
本身又创建了另一个装饰器@property.setter
,负责把setter方法编程属性直接赋值。
class Student(object):
@property
def score(self):
return self._score
@property.setter
def score(self, val):
if not isinstance(val, int):
raise ValueError('score must be an integer')
if val < 0 or val > 100:
raise ValueError('score must between 0-100')
self._score = val
这样可以把score当做public的属性来用。
>>> s = Student()
>>> s.score = 100 # equals s.score(100)
>>> s.score # equals s.score()
100
当只使用@property
而不使用@property.setter
时相关的变量就是只读属性。