本篇文章主要介绍面向对象编程。
面向对象编程
几个定义
- 面向过程编程:围绕函数,编写能处理数据的代码块的编程方式。
- 类、对象:面向对象编程的两个主要方面。一个类(Class)能创建一种新的类型;对象(Object)是类的实例。可这样类比:你可拥有类型 int 的变量,也即,存储整数的变量是 int类 的对象实例。
- 字段、方法和属性:从属于对象或类的变量叫字段(Field);对象调用从属于类的函数实现某些功能,这些函数叫类的方法(Method);字段和方法统称为累的属性(Attribute)。
Tips: 不同于静态语言,Python 中即使是整数也会被视为对象(int类的对象),同 Java 中装箱与拆箱概念颇为相似。
类和对象
最简单的类结构如下所示:
class Student(object):
pass # 一个空的代码块
# 创建类对象实例
student = Student()
print(student)
# 打印:<__main__.Student object at 0x101985f28>
*Tips: *
- Python 中所有类均继承自 object;
- Python 打印对象,默认可查看对象地址。Python 会在它找到的任何空间来存储对象。
类属性和实例属性
方法(Method)
Python 对比于 Java 类方法属性,有两个特殊点:
- 类中定义的所有方法,第一个参数必须是 self;相当于 Java 中的 this 引用;
- _init_() 方法:在类的对象被实例化时立即运行;相当于 Java 中的构造方法。
Tips: Python中不存在方法的重载,方法的重载是对同一个方法使用可变参数来实现,这在后面函数参数这块会提到。
示例代码如下:
class Student(object):
count = 0 # 类变量
def __init__(self, name):
self.name = name # 实例变量
def say_hi(self):
print(r'Hello, I'm', self.name)
student = Student('Coral')
student.say_hi()
# 打印:Hello, I'm Coral
字段(Field)
Python 中字段有两种类型——类变量与对象变量,对比上述示例代码,两者的区别点如下图所示:
因为 Python 是动态语言,可以给类创建的实例动态绑定属性,那么如何给 实例 绑定属性?如何给 类 绑定属性呢?
1)给实例绑定属性
- 通过在 _init_ 方法中给 self 绑定属性,这种属性所有对象实例均可访问;
- 直接给实例绑定,这种属性只适用于该实例,其他实例不能访问该属性。如:
student = Student('Coral')
# grade是给实例动态绑定的属性,name是通过 self绑定的属性
student.grade = 90
print('{} grade = {} '.format(student.name, student.grade))
student2 = Student('Jane')
print(student2.grade)
# 这句会抛出:AttributeError: 'Student' object has no attribute 'grade'
2)给类绑定属性
要让同一个字段属性对所有实例均适用,除了 self 绑定,还可以适用 类属性。
- 类属性,直接在 class 中定义属性,归整个 Student 类所有,如 count 字段。
Tips: 当 实例属性 和 类属性 同名时,实例属性 优于 类属性,也即,实力属性会覆盖类属性,所以在项目中要避免使用这种同名变量。
访问限制
Java 中有四种访问修饰符,Python 中并没有指定具体的访问修饰符,仅是按照一种约定的写法来,如下图所示:
获取对象信息
在 Java 中通常会判断一个对象是什么类型,用 instanceOf 来判断,Python 中也有判断对象类型的方法,还可以获取对象拥有的属性和方法,这些都是 Python 内置的方法。常见的如下图所示:
这里重点说下 isinstance() 方法,示例如下:
a = list() # a 是 list 类型
b = Animal() # b 是 Animal 类型
c = Dog() # c 是 Dog 类型
# 1.判断一个变量是否是某个类型可以用 isinstance() 判断:
print('a is list:', isinstance(a, list)) # True
print('b is Animal:', isinstance(b, Animal)) # True
print('c is Dog:', isinstance(c, Dog)) # True
print('c is Animal:', isinstance(c, Animal)) # True
# 2. 能用 type() 判断的基本类型也可以用 isinstance() 判断:
print(isinstance('a', str)) # True
print(isinstance(123, int)) # True
print(isinstance(b'a', bytes)) # True
# 3. 另外,isinstance() 还可以判断一个变量是否是某些类型中的一种:
# 判断变量是否是 list 或 tuple.
def is_list_or_tuple(x):
return isinstance(x, (list, tuple))
Tips: 区别于 type,对于 class 继承关系,不便使用 type();要判断 class 类型,使用 isinstance() 函数。因为 type() 只能返回对象本身的类型,比如 type(dog) != type(animal), 但是 isinstance(dog, Dog) = isinstance(dog, Animal)。
继承和多态
Python 中允许多继承,没有接口实现的概念。继承和多态总结点如下:
Python 中没有方法的重载,但是存在方法的覆盖,子类可以覆盖父类方法,也适用于运行时绑定传入的实例。
关于继承的几个问题
- 子类 能否继承 父类绑定的 属性?(每个类通过 self 或者 给实例动态绑定的 属性都是该类实例 独立拥有,跟子类没有任何关联?)
A: 子类不能继承 父类中通过 self或者 动态绑定的属性; - 子类 是否只能继承 父类绑定的 类属性?
A:子类能继承 父类中的 类属性,在操作该类属性时,使用 ChildClass.property 访问该类属性; - 子类可以继承 父类的所有方法?
A:Python 中方法没有 访问限制符修饰; - 每个类通过 self 或者 给实例动态绑定的 属性都是该类实例 独立拥有,跟子类没有任何关联?
A:是。 - 子类和父类有同名的 属性,优先使用哪个?(避免这种情况,直接继承使用父类的属性)
A:子类优先,但最好不要让子类属性和父类属性同名 。 - 子类能否继承父类中 __xxx 隐藏属性?
A:这个可以看做是每个类通过 self 绑定的属性均属于本类所有,父类中通过 self 绑定的属性不管是否是隐藏属性,子类直接访问就会抛出:AttributeError: 'Dog' object has no attribute '_BaseClass__name' .