面向对象编程(class)
一个对象包含了数据和操作数据的函数。
类和实例
1.类(Class)和实例(Instance),类是抽象的模板,eg:Student类,实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可能不同。
2.定义类是通过class
关键字,class
后面紧接着是类名,类名通常是大写开头的单词,紧接着是(object)
,表示该类是从哪个类继承下来的。
3.创建实例是通过类名+()实现的。
4.通过定义一个特殊的__init__
方法,在创建实例的时候,就把name
,score
等属性绑上去。
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
特殊方法“__init__”前后分别有两个下划线!!!
5.__init__
方法:①第一个参数永远是self
,表示创建的实例本身。所以,在__init__
方法内部,就可以把各种属性绑定到self
,因为self
就指向创建的实例本身。
②在创建实例的时候,就不能传入空的参数了,必须传入与__init__
方法匹配的参数,但self
不需要传,Python解释器自己会把实例变量传进去。
6.与普通的函数相比:在类中定义的函数第一个参数永远是实例变量self
,并且,调用时,不用传递该参数。
数据封装
1.可给Student
类增加新的方法,eg:get_grade,
可以直接在实例变量上调用,不需要知道内部实现细节。
2.类是创建实例的模板,而实例则是一个一个具体的对象,各个实例拥有的数据都互相独立,互不影响;
方法就是与实例绑定的函数,和普通函数不同,方法可以直接访问实例的数据。
访问限制
1.要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__
,在Python中,实例的变量名如果以__
开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问。
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))
改后:已经无法从外部访问实例变量.__name
和实例变量.__score
了
2.变成私有变量后,外部代码要获取,可用get_...方法。
class Student(object):
...
def get_name(self):
return self.__name
def get_score(self):
return self.__score
3.又要允许外部代码修改,可用set_...方法。
class Student(object):
...
def set_score(self, score):
self.__score = score
4.变量名__xxx__
,以双下划线开头,并且以双下划线结尾的特殊变量,特殊变量是可以直接访问的,不是private变量,所以,不能用__name__
、__score__
这样的变量名。
继承和多态
1.定义一个class,可从某个现有的class继承,新的class称为子类(Subclass),被继承的class称为基类、父类或超类(Base class、Super class)。
2.继承的好处:①子类获得了父类的全部功能,也可对子类增加一些方法。也可把父类不适合的方法覆盖重写。
class Dog(Animal):
def run(self):
print('Dog is running...')
def eat(self):
print('Eating meat...')
②多态。如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类。但是,反过来就不行。
class Dog(Animal):
def run(self):
print('Dog is running...')
class Cat(Animal):
def run(self):
print('Cat is running...')
Dog is running...
Cat is running...
3.“开闭原则”:①对扩展开放:允许新增Animal
子类。
②对修改封闭:不需要修改依赖Animal
类型的run_twice()
等函数。
4.继承树:
┌───────────────┐
│ object │
└───────────────┘
│
┌────────────┴────────────┐
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Animal │ │ Plant │
└─────────────┘ └─────────────┘
│ │
┌─────┴──────┐ ┌─────┴──────┐
│ │ │ │
▼ ▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ Dog │ │ Cat │ │ Tree │ │ Flower │
└─────────┘ └─────────┘ └─────────┘ └─────────┘
静态语言 vs 动态语言
1.静态语言,如果需要传入Animal
类型,则传入的对象必须是Animal
类型或者它的子类,否则,将无法调用run()
方法。
2.动态语言,不一定需要传入Animal
类型。我们只需要保证传入的对象有一个run()
方法就可以。
class Timer(object):
def run(self):
print('Start...')
获取对象信息
1.使用type()函数判断对象类型:①基本类型。
type(123)
<class 'int'>
type('str')
<class 'str'>
type(None)
<type(None) 'NoneType'>
②一个变量指向函数或者类。
type(abs)
<class 'builtin_function_or_method'>
type(a)
<class '__main__.Animal'>
type()
函数返回对应的Class类型。
2.使用isinstance()
函数①判断class的类型(继承关系),换句话说isinstance()
判断的是一个对象是否是该类型本身,或者位于该类型的父继承链上,②也可以判断的基本类型,③还可以判断一个变量是否是某些类型中的一种。
总是优先使用isinstance()判断类型,可以将指定类型及其子类“一网打尽”。
4.使用dir()
函数可获得一个对象的所有属性和方法,它返回一个包含字符串的list。
__xxx__
的属性和方法在Python中都是有特殊用途的。eg:__len__
方法返回长度。
len('ABC')
3
'ABC'.__len__()
3
实例属性和类属性
1.给实例绑定属性的方法是通过实例变量,或者通过self
变量。
ass Student(object):
def __init__(self, name):
self.name = name
s = Student('Bob')
s.score = 90
千万不要对实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,但是当你删除实例属性后,再使用相同的名称,访问到的将是类属性。
2.实例属性属于各个实例所有,互不干扰;
类属性属于类所有,所有实例共享一个属性;
不要对实例属性和类属性使用相同的名字,否则将产生难以发现的错误。