面向对象的基本可以用三个词来概括:
- 封装
- 多态
- 继承
而其中最重要的就是多态了,因为在java中已经火热的面向接口编程就是在多态的基础上进化而来的;
那么我们就在 java 的有理解的基础上来看在 Python 中是怎么实现的;
类和实例
这里再次强调,在面向对象中最重要的概念就是多态的抽象思想,而用编程语言是怎么实现的呢?那就是 类(Class) 和 实例(Instance) ,必须牢记抽象模板,比如 Student 类。
我用 Studebt 为例,在 Python 中定义类使用 class 关键字(其实在大部分的面向对象的语言中都是使用 class 关键来表明类) :
class Studedt(object):
pass
class
后面紧接着是类名,即Student
,类名通常是大写开头的单词,紧接着是(object)
,表示该类是从哪个类继承下来的,继承的概念我们后面再讲,通常,如果没有合适的继承类,就使用object
类,这是所有类最终都会继承的类。
>>> bart = Student()
>>> bart
<__main__.Student object at 0x10a67a590>
>>> Student
可以看到,变量bart
指向的就是一个Student
的实例,后面的0x10a67a590
是内存地址,每个 object 的地址都不一样,而Student
本身则是一个类。
由于类可以起到模板的作用的,因此可以在创建实例的时候,吧一些我们认为必须绑定的属性强制填写进去。通过 __init__
方法来实现:
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
注意:特殊方法 __init__
前后分别有两个下划线!!!
注意到 __init__
方法的第一个参数永远是 self
,表示创建的实例本身,因此,在 __init__
方法内部,就可以把各种属性绑定到 self
, 因为 self
就指向创建的实例本身。
有了 __init__
方法,在创建实例的时候,就不能传入空的参数了,必须传入与 __init__
方法匹配的参数,但 self
不需要传,Python 解释器自己会把实例变量传进去:
>>> bart = Student('Bart Simpson', 59)
>>> bart.name
'Bart Simpson'
>>> bart.score
59
和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量 self
, 并且,调用时,不用传递该参数。除此之外,类的方法和普通函数没有什么区别,所以,你仍然可以用默认参数、可变参数、关键字参数和命名关键字参数。
数据封装
面向对象的又一个重要的特点就是数据的封装;在 java 中我们是使用,我们可以定义一个在类生命周期中存在的对象,可以使用 __init__
来定义:
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))
调用:
>>> bart.print_score()
Bart Simpson: 59
限制访问
在 java 中有这权限关键字来定义类和方法的调用范围,而在 python 中又是怎么实现的呢?
在 Python 中我们使用的是在变量名之前加上 __
双下划线来表明是私有方法;
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
了:
>>> bart = Student('Bart Simpson', 59)
>>> bart.__name
Traceback (most recent call last):
File "", line 1, in
AttributeError: 'Student' object has no attribute '__name'
如果又要允许外部代码修改 score 怎么办?可以再给 Student 类增加set_score
方法:
class Student(object):
...
def set_score(self, score):
self.__score = score
你也许会问,原先那种直接通过bart.score = 99
也可以修改啊,为什么要定义一个方法大费周折?因为在方法中,可以对参数做检查,避免传入无效的参数:
class Student(object):
...
def set_score(self, score):
if 0 <= score <= 100:
self.__score = score
else:
raise ValueError('bad score')
继承和多态
继承和多态是面向对象的最重要的特性,当然还有抽象和封装这里我们就不展开讲。
继承
在 Java 中我们如何开进行继承呢?
class Fun{
public void run(){
}
}
class Zi extends Fun{
@Override
public void run() {
super.run();
}
}
而在我们的 Python 中我们又该如何来实现呢?
class Animal(object):
def run(self):
print('Animal is running...')
class Dog(Animal):
pass
class Cat(Animal):
pass
dog = Dog()
dog.run()
cat = Cat()
cat.run()
运行结果为:
Animal is running...
Animal is running...
当然我们在实际开发中肯定不会只使用父类中的方法,肯定会复写父类中的方法:
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...
多态
Python 中多态是如何实现的呢?
a = list() # a是list类型
b = Animal() # b是Animal类型
c = Dog() # c是Dog类型
>>> isinstance(a, list)
True
>>> isinstance(b, Animal)
True
>>> isinstance(c, Dog)
True
ps:韩说也可以用来规定输入类型:
def run_twice(*s):
for i in s:
if(isinstance(i,Animal)):
i.run()
else:
print('xxxxxxxxxxx')
def run_twice2(*s:Animal):
for i in s:
i.run()
获取对象信息
type()
在 python 中 type()
主要是用来判断基本类型。
isinstance()
上面的代码中我们就使用了 isinstance()
来进行类型判断,主要用来判断给定类型,一般使用中总是优先使用isinstance()判断类型,可以将指定类型及其子类“一网打尽”。
dir()
如果要获得一个对象的所有属性和方法,可以使用dir()
函数,它返回一个包含字符串的list;
在使用用反射时使用。
实例属性和类属性
因为 Python 属于动态语言,那么我们可以根据类创建的实例可以任意绑定属性;
给实例绑定属性的方法是通过实例变量,或者通过self
变量:
class Student(object):
def __init__(self, name):
self.name = name
s = Student('Bob')
s.score = 90
或者我们也可以:
class Student(object):
name = 'Student'
s = Student()
print(s.name)
print(Student.name)
s.name = 'Michael'
print(s.name)
print(Student.name)
del s.name
print(s.name)
运行后结果:
Student
Student
Michael
Student
Student