Python是也是一个面对对象编程(Object-Oriented Programming)的语言,面对对象编程是一种设计思想,意味着我们把对象作为程序的基本单元,而每个对象包含了自己的属性和方法。面向对象编程主要有以下特点:
- 封装(Encapsulation):对外部世界隐藏对象的工作细节。
- 继承(Inheritance):继承使子类具有父类的各种属性和方法,而不需要编写相同的代码。
- 多态(Polymorphism):为不同的数据类型的实体提供统一的接口。
使用OOP有以下的优点:
- 提高软件开发的生产效率
- 使软件的可维护性更好
- 提高软件的质量
在 Python 中,元组、列表和字典等数据类型是对象,函数也是对象。那么,我们能创建自己的对象吗?Of Course!跟其他 OOP 语言类似,我们使用类(class)来自定义对象。
类和实例(Class, Instance)
每个类都有自己的属性(attribute)和方法(method),比如一个人的身高、体重和年龄,这些都是属性,而吃饭、说话和洗澡都是方法。(要注意:在class外部定语的可执行函数叫做function,类内部的函数叫做方法method)
类的定义以class为开头,类名的首字母推荐要大写,冒号之后换行缩进紧跟着属性和方法的定义,属性无非就是一个变量的定义,而方法的定义和函数的定义是一样的,也是以def开头。请看下面的例子:
# class
class Person:
# attribute fields
name = 'William'
age = 45
# method
def greet(self):
print("Hi, my name is " + self.name)
# Create an Object
p1 = Person()
# Call the method
p1.greet()
类的定义是一个具体实例(instance)的设计蓝图,在创建实例的时候,我们只要调用类名,然后加括号就可以了。在greet方法中,我们使用了特殊参数self,它永远指向创建的实例本身,所以self.name就会指向当前被创建实例的name属性。p1.greet()是方法调用的示范,我们只要在实例名后加上句号(.)紧跟着方法名,即可调用实例的方法。
我们也可以在创建好实例之后,对它的属性和方法进行修改:
# Modify Object Properties
p1.age = 40
# Delete Object Properties
del p1.age
# Delete Objects
del p1
__init__
是 Python 中的特殊方法(special method),它用于初始化对象。它是一个实例被创建时最先被调用的函数,并且每次创建实例,它的__init__都会被调用,而且它的第一个参数永远是 self
,指向创建的实例本身。(init是initial的简写,顾名思义就是用来初始化的)
class Person:
def __init__(self):
self.name = 'Alice'
def greet(self):
print("Hi, my name is " + self.name)
p1 = Person()
p1.greet()
我们也可以在__init__方法中添加其他参数,这样我们的的初始化能更加灵活和方便,同时在创建实例的时候,需要传入与__init__方法匹配的参数:
class Person:
def __init__(self, init_name):
self.name = init_name
def greet(self):
print("Hi, my name is " + self.name)
p1 = Person("David")
p1.greet()
继承和多态(Inheritance,Polymorphism)
继承
在面向对象编程中,当我们已经创建了一个类,而又想再创建一个与之相似的类,比如添加几个方法,或者修改原来的方法,这时我们不必从头开始,可以从原来的类派生出一个新的类,我们把原来的类称为父类或基类,而派生出的类称为子类,子类继承了父类的所有数据和方法。
让我们看一个简单的例子,首先我们定义一个 Animal 类:
class Animal():
def __init__(self, name):
self.name = name
def greet(self):
print('Hello, I am %s.' % self.name)
现在,我们想创建一个 Dog 类,比如:
class Dog():
def __init__(self, name):
self.name = name
def greet(self):
print('WangWang.., I am %s. ' % self.name)
可以看到,Dog 类和 Animal 类几乎是一样的,只是 greet
方法不一样,我们完全没必要创建一个新的类,可以直接创建子类(child class)来继承父类Animal:
class Dog(Animal):
def greet(self):
print('WangWang.., I am %s. ' % self.name)
Dog 类是从 Animal 类继承而来的,Dog 类自动获得了 Animal 类的所有数据和方法,而且还可以对从父类继承来的方法进行修改,调用的方式是一样的:
animal = Animal('animal')
animal.greet()
dog = Dog('dog')
dog.greet()
我们也可以在Dog 类中添加新的方法:
class Dog(Animal):
def greet(self):
print('WangWang.., I am %s. ' % self.name)
def run(self):
print('I am running!')
dog = Dog('dog')
dog.greet()
多态
多态的概念其实不难理解,它是指对不同类型的参数进行相同的操作,根据对象(或类)类型的不同而表现出不同的行为。继承可以拿到父类的所有数据和方法,子类可以重写父类的方法,也可以新增自己特有的方法。有了继承,才有了多态,这样才能实现为不同的数据类型的实体提供统一的接口。
class Animal():
def __init__(self, name):
self.name = name
def greet(self):
print(f'Hello, I am {self.name}.')
class Dog(Animal):
def greet(self):
print(f'WangWang.., I am {self.name}.')
class Cat(Animal):
def greet(self):
print(f'MiaoMiao.., I am {self.name}')
def hello(animal):
animal.greet()
可以看到,cat
和 dog
是两个不同的对象,对它们调用 greet
方法,它们会自动调用实际类型的 greet
方法,作出不同的响应:
dog = Dog('dog')
hello(dog)
cat = Cat('cat');
hello(cat)
Iterators
在某些情况下,我们希望实例对象可被用于for...in
循环,这时我们需要在类中定义__iter__
和__next__
方法。其中,__iter()__
方法返回迭代器对象本身__next()__
方法返回容器的下一个元素,在没有后续元素时会抛出StopIteration
异常。(Python 的 for
循环实质上是先通过内置函数 iter()
获得一个迭代器,然后再不断调用 next()
函数实现的。)
class Fib():
def __init__(self):
self.a, self.b = 0, 1
def __iter__(self):
return self
def __next__(self):
self.a, self.b = self.b, self.a + self.b
return self.a
fib = Fib()
for i in fib:
if i > 10:
break
print(i)# 1, 1, 2, 3, 5, 8
访问限制 underscore
在某些情况下,我们希望限制用户访问对象的属性或方法,也就是希望它是私有的,对外隐蔽。比如,对于上面的例子,我们希望 name
属性在外部不能被访问,我们可以在属性或方法的名称前面加上两个下划线,即 __
,以下是对之前例子的改动:
class Animal():
def __init__(self, name):
self.__name = name
def greet(self):
print(f'Hello, I am self.__name.')
animal = Animal('a1')
animal.__name # error
需要注意的是,在 Python 中,以双下划线开头,并且以双下划线结尾(即 __xxx__
)的变量是特殊变量,特殊变量是可以直接访问的。所以,不要用 __name__
这样的变量名。另外,如果变量名前面只有一个下划线_,表示此变量不要随便访问,虽然它可以直接被访问。
模块调用
有时候一个模块中放不了许多的类,那么我们就要把一些类放入其他的模块中,当我们需要那些类的时候,只需要调用对应模块即可。创建一个模块很简单,只要把代码保存在一个文件中,然后加上后缀.py即可。你可以任意命名文件名,但是必须以.py为后缀。以下我们在animal.py文件中定义了多个不同的类:
# animal.py
class Animal():
def __init__(self, name):
self.name = name
def greet(self):
print(f'Hello, I am {self.name}.')
class Dog(Animal):
def greet(self):
print(f'WangWang.., I am {self.name}.')
class Cat(Animal):
def greet(self):
print(f'MiaoMiao.., I am {self.name}')
如果要在其他文件中调用单个类的不同方法,使用import语句即可:
from animal import Animal
animal = Animal('animal')
animal.greet()
调用多个类:
from animal import Dog, Cat
dog = Dog('duoduo')
dog.greet()
cat = Cat('Kitty')
cat.greet()
其他调用方法:
# importing an entire module
import animal
cat = animal.Cat('Kitty')
# import all classes from a model
from animal import *
cat = Cat('Kitty')
# Using Aliases
from animal import Cat as C
cat = C('Kitty')
Python标准库(Python Standard Library)
现在我们已经对函数和类有基础的认识了,我们来聊聊如何使用别人编写好的库,Python有自己的标准库,我们下载Python的时候,它们已经被默认安装了,其中有很多现成的模块供我们调用,我们试一下用random模块做做一些随机数操作:
from random import randint, choice
# Generate a random number between 1 and 6
print(randint(1, 6))
players = ['alice', 'david', 'charles', 'michael']
# choose a randomly chosen element
random_pick = choice(players)
print(random_pick)
PIP包管理器
有的时候,我们想要使用别人编写好的模块,我们可以通过包管理器下载别人的包(package),包是由很多module组成的,来实现某种功能。库(library)是抽象概念,也可以是各种模块组成。而Python中最流行的包管理器就是pip。pip3的安装请大家自行搜索,网上有很多的教程,以下我会使用一个和图像处理相关的模块。
PIL(Python Image Library)是Python中的标准图像处理库。PIL功能强大,而且API非常简单易用。由于PIL仅支持到Python 2.7,加上年久失修,于是一群志愿者在PIL的基础上创建了兼容的版本,名字叫Pillow,支持最新Python 3.x,又加入了许多新特性,因此,我们可以直接安装使用Pillow。以下是安装pillow的命令行:
pip3 install pillow
然后我们可以创建一个Python,使用PIL的模块,写出一段可以把照片弄模糊的代码:
from PIL import Image, ImageFilter
# Open an image
im = Image.open('test.jpg')
# Use bluring filter
im2 = im.filter(ImageFilter.BLUR)
im2.save('blur.jpg', 'jpeg')
包管理器让我们可以直接使用前人的轮子,简化我们的开发过程。
类的编写规范 Styling Classes
类的名字最好使用骆驼命名法(CamelCase),也就是让每个单词的第一个字母大写,不使用下划线分割单词。实例和模块的名字最好都使用Snake case,也就是所有字母都小写,然后使用下划线分割。当然这不是强制的,但这是工业界比较合理的命名规范,大家可以参照一下Google的Python代码规范:http://google.github.io/styleguide/pyguide.html
您可以使用空行来组织代码,但不要过度使用它们。在一个类中,您可以在方法之间使用一个空行,而在一个模块中,您可以使用两个空行来分隔类。
如果需要从标准库和编写的模块中导入模块,请首先将标准库模块的调用语句写在最前面。然后添加一个空行,再调用自己编写的模块。在具有多个import语句的程序中,此约定使查看程序中使用的不同模块的来源更加容易。
课后练习
创建一个Car类,其中包含brand,model,year属性。这个类初始化的时候必须要传入品牌名字(比如Subaru),初始化的时候车的型号和生产年会被默认设定为’xxx’和0,Car类还有内置方法可以修改车型号和生产年,还有可以打印出完整车信息的方法。
请根据要求定义出这个完整的类,并在另一个文件调用此类,创建一个实例,然后使用内置方法修改车的型号和生产年,最后使用类的方法打印出车的完整信息。
# car.py
class Car:
brand, model, year
def __init__(self, brand):
self.brand = brand
model = 'xxx'
year = 0
def set_model(self, model):
self.model = model
def set_year(self, year):
self.year = year
def get_info(self):
print(f"Brand: {self.brand}, Model: {self.model}, Year: {self.year}")
# main.py
from car import Car
new_car = Car("Subaru")
new_car.set_model("WRX")
new_car.set_year(2014)
new_car.get_info()