本系列笔记面向没有任何Python基础的读者。笔记的主要内容和代码来源于《Python编程从入门到实践》,欢迎大家讨论和指出笔记中的问题。
前言
类是Python面向对象编程的重要内容,其主要功能就是把数据与功能绑定在一起。创建新类就是创建新的对象类型,从而创建该类型的新实例 。类实例支持维持自身状态的属性,还支持(由类定义的)修改自身状态的方法。本篇笔记将通过几个例子帮助读者快速了解Python类的使用方法
目录
9.1 创建和使用类
面向对象编程的核心概念就是创建和操作对象来完成一系列动作,其中涉及到对象和类这一组重要的概念。简而言之,对象(object)是具体的实例,比如柴犬、柯基、哈士奇等,他们有自己独特的品种、性别、毛发颜色等属性,可以进行吃饭、睡觉(涩涩)等行为。而类(class)是抽象的概括,比如柴犬、柯基、哈士奇都可以归为“狗”这一类别,而他们都有一些共同的属性和行为,当我们把这些属性和行为概括起来,就形成了“类”。
下面我们用一个Dog类来说明如何在Python中创建类。
dog.py
class Dog():
"""A simple attempt to model a dog."""
def __init__(self, name, age):
"""Initialize name and age attributes."""
self.name = name
self.age = age
def sit(self):
"""Simulate a dog sitting in response to a command."""
print(self.name.title() + " is now sitting.")
def roll_over(self):
"""Simulate rolling over in response to a command."""
print(self.name.title() + " rolled over!")
my_dog = Dog('willie', 6)
your_dog = Dog('lucy', 3)
print("My dog's name is " + my_dog.name.title() + ".")
print("My dog is " + str(my_dog.age) + " years old.")
my_dog.sit()
print("\nMy dog's name is " + your_dog.name.title() + ".")
print("My dog is " + str(your_dog.age) + " years old.")
your_dog.sit()
1. 创建类
类的名称首字母是大写,从空白创建类的话,类定义中的括号是空的
2. 创建方法
方法 __init()__
类中的函数统称为方法,__init()__
在根据Dog类创建实例时都会自动运行。该方法包含3个形参:self, name, age
。其中self
是必不可少的,并且要位于最前面,当Python调用__init()__
方法创建Dog实例时,将自动传入实参self
,它是一个指向实例本身的引用,能让实例访问类中的属性和方法。
以self
为前缀的变量都可以供类中的所有方法使用,并且可以通过类的任何实例来访问这些变量。self.name = name
获取存储在形参name
中的值,并将其存储到变量name
中,然后该变量被关联到当前创建的实例,这样的可以通过实例访问的变量称为属性。
其他的两个方法只有一个形参self
。
3. 创建实例
my_dog = Dog('willie', 6)
,实参是“willie”和“6”,方法__init()__
创建一个表示特定小狗的示例,并参数设置name
和age
,并未显式地包含return
语句,但是会返回这条小狗的实例。
实例的命名规则:用首字母大写的名称指类,小写字母的名称是根据类创建的实例
4. 访问属性
使用句点表示法访问实例的属性 my_dog.name
,找到实例my_dog
,再查找与这个实例相关联的属性name
。在Dog类中引用这个属性时,使用的是self.name
5. 调用方法
使用句点表示法调用方法my_dog.sit()
6. 创建多个实例
可以根据需要创建任意数量的实例
9.2 使用类和实例
当我们创建了类之后,可以对它的属性、方法进行一定的修改来让这个类拥有更多功能。
car.py
"""A class that can be used to represent a car."""
class Car():
"""A simple attempt to represent a car."""
def __init__(self, manufacturer, model, year):
"""Initialize attributes to describe a car."""
self.manufacturer = manufacturer
self.model = model
self.year = year
self.odometer_reading = 0
def get_descriptive_name(self):
"""Return a neatly formatted descriptive name."""
long_name = str(self.year) + ' ' + self.manufacturer + ' ' + self.model
return long_name.title()
def read_odometer(self):
"""Print a statement showing the car's mileage."""
print("This car has " + str(self.odometer_reading) + " miles on it.")
def update_odometer(self, mileage):
"""
Set the odometer reading to the given value.
Reject the change if it attempts to roll the odometer back.
"""
if mileage >= self.odometer_reading:
self.odometer_reading = mileage
else:
print("You can't roll back an odometer!")
def increment_odometer(self, miles):
"""Add the given amount to the odometer reading."""
self.odometer_reading += miles
1. 给属性指定默认值
类中的每个属性都必须有初始值,可以在方法__init()__
内指定初始值,在括号内设置形式参数,如果没有在创建实例时设置,则会用默认的形式参数
2. 修改属性的值
(1)直接修改属性的值
通过实例直接访问并进行修改:my_car.odometer_reading = 23
(2)通过方法修改属性的值
def update_odometer(self, mileage):
"""
Set the odometer reading to the given value.
Reject the change if it attempts to roll the odometer back.
"""
if mileage >= self.odometer_reading:
self.odometer_reading = mileage
else:
print("You can't roll back an odometer!")
该方法可以接受一个里程值,储存到相应变量中,并且增加了一个禁止回调的功能
(3)通过方法对属性的值进行递增
def increment_odometer(self, miles):
"""Add the given amount to the odometer reading."""
self.odometer_reading += miles
该方法接受一个值可以修改属性
9.3 继承
一个类继承另一个类时,将自动获得另一个类的所有属性和方法,被继承的类称为父类,而新类被称为子类
electric_car.py
from car import Car
class Battery():
"""A simple attempt to model a battery for an electric car."""
def __init__(self, battery_size=60):
"""Initialize the batteery's attributes."""
self.battery_size = battery_size
def describe_battery(self):
"""Print a statement describing the battery size."""
print("This car has a " + str(self.battery_size) + "-kWh battery.")
def get_range(self):
"""Print a statement about the range this battery provides."""
if self.battery_size == 60:
range = 140
elif self.battery_size == 85:
range = 185
message = "This car can go approximately " + str(range)
message += " miles on a full charge."
print(message)
class ElectricCar(Car):
"""Models aspects of a car, specific to electric vehicles."""
def __init__(self, manufacturer, model, year):
"""
Initialize attributes of the parent class.
Then initialize attributes specific to an electric car.
"""
super().__init__(manufacturer, model, year)
self.battery = Battery()
1. 子类继承父类的方法和属性
子类的__init()
方法
class ElectricCar(Car):
def __init__(self, manufacturer, model, year):
super().__init__(manufacturer, model, year)
super()
函数将子类与父类的所有属性关联起来,父类也称超类(superclass)。
2. 子类定义新的方法和属性
和父类添加方法和属性的过程一样,没有限制。
3. 重写父类的方法
在子类中定义一个与要重写的父类方法名称相同的方法
4. 将实例用作属性
将大型类拆分成多个协同工作的小类,比如在电动车类中增加电池类,将大类进行剖分,例如Battery()
类。在ElectricCar
类中,添加一个self.battery
的属性可以在电动车类中创建一个Battery实例,并储存在self.battery
属性中,每次方法被调用时都会创建一个电池实例,当需要使用电池实例的属性时,调用:my_tesla.battery.describe_battery()
。
9.4 导入类
通过前面几个小节,我们已经知道了怎样创建以及使用类。那如果我们写好了一个类,想要把它用在其他的文件中的时候,就需要用到导入类的技巧。导入类是模块化编程的一个重要知识点。
1. 导入单个类
在文档的开头添加注释来增加模块级文档字符串,使用import
语句打开模块并导入:
from car import Car
2. 在一个模块中存储多个类
在模块中存储多个类,通过import
导入模块中不同的类
3. 从一个模块中导入多个类
用逗号分隔各个类
from car import Car. ElectricCar
4.导入整个模块
不使用from
来导入整个模块,再用句点表示法访问需要的类(注意句点是一定需要的)
import car
my_beetle = car.Car('volkswagen', 'beetle'. 2016)
5. 导入模块中的所有类
使用星号表示所有类,但是不推荐使用,可能会重名导致错误
from car import *
6. 在一个模块中导入另一个模块
当模块分散程度比较高时,在模块中使用import语句导入另一个模块。
9.5 Python标准库
Python为我们提供了很多可以直接使用的库,通过上面导入类的方法,可以将他们导入到自己的程序中。
from collections import OrderedDict
favorite_languages = OrderedDict()
favorite_languages['jen'] = 'python'
favorite_languages['sarah'] = 'c'
favorite_languages['edward'] = 'ruby'
favorite_languages['phil'] = 'python'
for name, language in favorite_languages.items():
print(name.title() + "'s favorite language is " +
language.title() + ".")
模块Collections中的OrderedDict可以用来创建有序字典,并储存在变量中。
9.6 类编码风格
类名采用驼峰命名法:将类名中的每个单词的首字母都大写,而不使用下划线。实例和模块名都采用小写格式,并在单词之间加上下划线
对于每个类,都应紧跟在类定义后面包含一个文档字符串,简要的描写类的功能,并遵循编写函数的文档字符串采用的格式约定。每个模块都应包含一个文档字符串
可以用空行来组织代码:在类中使用一个空行来分隔方法;在模块中用两个空行来分隔类
需要同时导入标准库和自己编写的模块时,先编写导入标准库的import语句,在添加一个空行,导入自己编写的模块语句
代码总结
my_car.py 创建汽车实例
from car import Car
my_new_car = Car('audi', 'a4', 2015)
print(my_new_car.get_descriptive_name())
my_new_car.odometer_reading = 23
my_new_car.read_odometer()
my_cars.py 创建多个汽车实例
from car import Car
from electric_car import ElectricCar
my_beetle = Car('volkswagen', 'beetle', 2015)
print(my_beetle.get_descriptive_name())
my_tesla = ElectricCar('tesla', 'roadster', 2015)
print(my_tesla.get_descriptive_name())
python通过命名的起始处下划线来区分是private变量,protect变量还是public变量。
权限名称 | 权限控制 | 起始标志 |
---|---|---|
public | 可见 外部可以访问 | 无 _ |
protect | 不可见 外部可以访问 | _(单下划线) |
private | 不可见 不可访问 | __ (双下划线) |
class TestIsVisible(object):
def __init__(self, visible, invisible, non_know):
self.visible = visible
self.__invisible = invisible
self._visible = non_know
def hello_world(self):
dic = dict()
dic["hello"] = "world!"
print dic
def __hello_china(self):
dic = dict()
dic["hello"] = "china"
print dic
if __name__ == '__main__':
visible_object = TestIsVisible("can see", "can not see", "non know if visible")
print visible_object.visible
print visible_object._visible
print visible_object.__dict__
print dir(visible_object)
print visible_object.__invisible