2018-6-6
序
面向对象编程——Object Oriented Programming,简称OOP。
程序的基本单元:对象
对象:包含了数据和操作数据的函数
在Python中,万物皆为对象。(程序员后宫佳丽三千哟)
所有的数据类型都可视为对象,
对象也可以是自己创造的。
自己创造—自定义的对象数据类型就是类(class)。
Class是一种抽象概念,比如我们定义的Class——Student,是指学生这个概念,而实例(Instance)则是一个个具体的Student。
面向对象的设计思想是抽象出Class,根据Class创建Instance。
类和实例
class Student(object):
pass
class
后面跟的是类名——Student
,注意,类名通常是大写开头的单词。
后面的(object)
,表示这个类是从哪个类继承下来的。如果没有合适的继承类,就使用object
类,这是所有类最终都会继承的类。【想想树枝】
类定义以后就可以创建其实例:
bart = Student()
变量bart
指向的就是一个Student
的实例。
可以给实例变量bart
绑定一个属性name
:
bart.name = 'Bart Simpson'
啊,类就像女娲捏泥人儿的模子,女娲捏人儿(创建实例)的时候,就得把一些个她认为必须绑定的属性强制填写进去。那她就用了__init__
方法:
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
应当注意,
注意到__init__
方法的第一个参数永远是self
,表示创建的实例本身,因此,在__init__
方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。
数据封装
一个实例本身是拥有一些属性的数据的,可以通过函数来访问。
但是可以通过Student
类的内部定义访问数据的函数,这样子,就相当于把数据给装到黑匣子了嘛~并且这些封装数据的函数是和Student
类是关联的,人们给了它一个名字——类的方法。这是数据封装的第一个好处。
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
def print_score(self):
print('%s: %s' % (self.name, self.score))
数据封装的第二个好处是:
可以给Student
类增加新的方法,比如get_grade
:
class Student(object):
...
def get_grade(self):
if self.score >= 90:
return 'A'
elif self.score >= 60:
return 'B'
else:
return 'C'
2018-6-7
访问限制
在Python中,实例的变量名如果以__
开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问。
def print_score(self):
print('%s: %s' % (self.__name, self.__score))
啊,这样的话连访问都不可以了,如果需要访问的话需要给Student类增加get_name
和get_score
这样的方法。
class Student(object):
...
def get_name(self):
return self.__name
def get_score(self):
return self.__score
这时候脑子抽了又想允许外部代码修改score了(事儿多得很),这时候可以增加set_score
方法。
class Student(object):
...
def set_score(self, score):
self.__score = score
继承和多态
在OOP中,我们可以通过一个现有的class继承来定义一个class,这个新的class就称为子类——subclass,现有的被继承的class称为基类、父类或者超类——Base class、Super class。
class Animal(object):
def run(self):
print('Animal is running...')
class Dog(Animal):
pass
class Cat(Animal):
pass
上面的例子中,Animal
是父类,Dog
和Cat
就是子类。
这样做的目的是,子类拥有了父类全部的功能——在本例中实现了run()
方法。
2018-6-9
北京,雨
多态的好处:
对于一个变量,我们只需要知道它是什么父类型,而无需知道它的子类型,就可以使用其方法——调用方只需要调用,而不需要知道细节。在增加一种子类时,只需要保证方法正确,不用管原来的代码是如何调用的。也就是“开闭原则”:
对扩展开放:允许新增
Animal
子类;
对修改封闭:不需要修改依赖Animal
类型的run_twice()
等函数。
从具体理解上看,继承就像是树一样:
静态语言 vs 动态语言
对于静态语言(例如Java)来说,如果需要传入
Animal
类型,则传入的对象必须是Animal
类型或者它的子类,否则,将无法调用run()
方法。
对于Python这样的动态语言来说,则不一定需要传入Animal
类型。我们只需要保证传入的对象有一个run()
方法就可以了:
class Timer(object): def run(self): print('Start...')
获取对象信息
type()
判断对象是哪种基本数据类型,还是函数
>>> type(123)==int
True
>>> import types
>>> def fn():
... pass
...
>>> type(fn)==types.FunctionType
True
>>> type(abs)==types.BuiltinFunctionType
True
>>> type(lambda x: x)==types.LambdaType
True
>>> type((x for x in range(10)))==types.GeneratorType
True
isinstance()
原文称:对于class的继承关系来说,使用type()
不方便。
为什么呢?
Python type() 函数中谈及isinstance()
与type()
区别:
- type() 不会认为子类是一种父类类型,不考虑继承关系。
- isinstance() 会认为子类是一种父类类型,考虑继承关系。
如果要判断两个类型是否相同推荐使用 isinstance()。
dir()
可以获取一个对象的所有属性和方法。
>>> dir('ABC')
['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill']
配合getattr()
、setattr()
以及hasattr()
,可以直接操作一个对象的状态。
getattr()
:用于返回一个对象的属性值——getattr(object, name[, default])
setattr()
:用于设置属性值,该属性必须存在——setattr(object, name, value)
hasattr()
:用于判断对象是否包含对应的属性——hasattr(object, name)
实例属性和类属性
实例属性属于各个实例所有,互不干扰;
类属性属于类所有,所有实例共享一个属性;
不要对实例属性和类属性使用相同的名字,否则将产生难以发现的错误。
为了统计学生人数,可以给Student类增加一个类属性,每创建一个实例,该属性自动增加:
class Student(object):
count = 0
def __init__(self, name):
self.name = name
Student.count += 1