对象封装在我上大学的时候是一个热门的话题,那时候大部分的编程习惯都是以面向过程编程,上大学的时候学Java,不断地提到就是面向对象编程,直至目前面向对象编程仍是基石之一。除了写快速的脚本,在实际项目中面向对象使用单继承封装了大量的易用函数,会让新手接触业务更容易更简单。
1. 类基本介绍
1.1. 类定义
类是什么?以人来说,每个人都有手、有脚、会吃饭会思考,那么人就是一个类,但要结合实例来理解,每个人都是一个人这样的一个类的实例,每个人的身高、体重都不一样,保存着不同的个人化数据。用真正的术语来说类和实例,下面就是维基百科的对类这个计算机科学术语的定义。
类: 由某种特定的元数据所组成的内聚的包。它描述了一些对象的行为规则,而这些对象就被称为该类的实例。
类一般包含属性和方法,Python的定义一个类如下:
class Person(object):
def __init__(self, arg):
super(Person, self).__init__()
self.arg = arg
# 类的实例
tom = Person("")
上面用class关键字定义了一个Person类,object是Python整个类体系的根类,这里表示Person继承object,object是Person的父类,Person是object的子类,tom是Person的一个实例。
1.2. 类的属性
类的属性有分类变量和实例变量。
类变量:全部实例都可以访问同一个变量,但实例去修改的话,其实是绑定新的一个变量,不能作用到全部实例上面。一般使用类名去修改。
class Person(object):
age = 10
tom = Person()
mary = Person()
print(Person.age)
print(tom.age)
print(mary.age)
Person.age=10
print(Person.age)
print(tom.age)
print(mary.age)
tom.age = 20
print(Person.age)
print(tom.age)
print(mary.age)
0
0
0
10
10
10
10
20
10
age是Person的类变量,所有实例都会有这个变量,但一般修改这个变量值都是用Person.age来修改,这样才会一起修改,但tom.age这样处理的时候,其实绑定了一个新的变量,tom.age和Person、mary的都不一样的。
实例变量:每个实例都有自己的变量,互相不影响是平常最常用的变量,一般用self
关键词来引用,self
是和其他语言的this
关键词一样,表示当前实例。
class Person(object):
def __init__(self, age):
super(Person, self).__init__()
self.age = age
# 一般在__init__方法里面定义和初始化,在类内
tom = Person(20)
print(tom.age)
20
mary = Person(18)
print(mary.age)
18
可以看到每个实例都有自己实例变量,一般都把变量放在__init__
方法里面进行初始化。而私有变量和公有变量怎么设置呢?一般变量前面加双下划线__
,如__age
就是私有变量,不加的就是公有变量。在实际编写情况中,很多都是忽略加下划线的,但最佳实践我觉得是统一习惯,假如一开始大家都是以双下划线来表示私有变量,那么都用这种规则,但如果一开始没有规定,项目内已经乱象丛生了,那么就统一或者放任自流都可以,但对于公用类库不能这样,需要认真考虑和编写。
由于Python的简便性,很多时候都不写setter和getter,很多人就埋怨Python不正规导致很容易出错,但实际上,setter和getter这些并不是一加上去就可以包治百病,变量直接使用只要得当也不会出问题,那究竟什么是最佳实践呢?基于时间和人力考虑,如果是业务性代码,维护起来基本上单人维护的,那我觉得怎么简便怎么来,因为这些代码很多时候都是快餐式一次性的代码,够快够稳就好了,不考虑太多其他因素。如果是基础库,我觉得就需要谨慎了,多斟酌如何写,多考虑使用者的情况才能写出好的类库。
1.3. 类的方法
1.3.1. 成员方法
成员方法和函数差不多,但需要传入self参数,因为实例调用方法的时候回默认传入实例自己进去,所以编写起来需要注意self的使用就好:
class Person(object):
def __init__(self, total_distance):
super(Person, self).__init__()
self.total_distance = total_distance
def run(self, distance):
self.total_distance += distance
print("run {0} meters, total run {1} meters".format(distance, self.total_distance))
person = Person(0)
person.run(10)
person.run(2)
person.run(20)
run 10 meters, total run 10 meters
run 2 meters, total run 12 meters
run 20 meters, total run 32 meters
run方法就是person的成员函数,调用它的时候不能用Person.run(10)
,需要实例对象来调用。
1.3.2. 静态方法
静态方法就是实例对象和类对象都可以调用的方法。使用方式如下:
class Person(object):
def __init__(self, total_distance):
super(Person, self).__init__()
self.total_distance = total_distance
@staticmethod
def run(distance):
print("run {0} meters".format(distance))
person = Person(0)
person.run(10)
Person.run(20)
@staticmethod
是一个修饰符,后面再说到关于修饰符的内容。只需要记住类成员函数加了这个修饰符就是静态函数。而且静态函数不需要传入self,因为它不是没用到成员变量。
1.3.3. 类方法
类方法就是用@classmethod
修饰,第一个参数传入cls
的函数,看下面例子:
class Person(object):
tablename = "person"
@classmethod
def run(cls, distance):
print("classname {0} tablename {1} run {2} meters".format(cls.__name__, cls.tablename, distance))
person = Person()
person.run(10)
Person.run(20)
classname Person tablename person run 10 meters
classname Person tablename person run 20 meters
cls可以获取到类的一些变量和函数。
1.3.4. 三种方法的区别
一般用到实例相关的成员属性就肯定使用成员方法的了,所以大部分情况下都会默认使用成员方法。如果像写工具类方法的时候,可以大部分使用静态方法,其实这样和一个module里面写一个方法差不多。
# utils.py
def sf_int(num, default=None):
pass
# other_utils.py
class IntUtil(object):
@staticmethod
def sf_int(num, default=None):
pass
# main.py
import utils
sf_int("20", 1)
from other_utils import IntUtil
IntUtil.sf_int("20", 1)
上面这样一对比,其实差别不大,方便的话在module里面直接写也和class差不多。最后是类方法,这个真的很少会用到,但继承的时候用到了这个。因为继承的时候cls会变化,这时候重新定义每个类的类变量的时候,cls获取的类变量也可以变化,这样类方法不用变就调用父类的类方法。例子如根据类的类变量生成一个key值,例子会在继承给出。
1.4. 初始化方法和析构方法
初始化方法一般用于实例初始化的时候赋值一些成员属性,像每个人都有自己的年龄,初始化的时候先赋值给每个人的年龄。使用的是__init__
方法
class Person(object):
def __init__(self, age):
super(Person, self).__init__()
self.age = age
# 一般在__init__方法里面定义和初始化,在类内
tom = Person(20)
super
是调用了父类的方法,所以这里是调用了父类的初始化方法__init__
。
析构方法就是实例被销毁的时候调用的,但其实很少使用__del__
方法,说是用来释放某些资源,但往往很少用到,还不如在程序中实际上调用释放资源更加放心。
class Person(object):
def __init__(self, age):
super(Person, self).__init__()
self.age = age
def __del__(self):
print("deleting..")
person = Person(20)
del person
2. 总结
类系统都是非常复杂,这只是简单介绍。在源码级别分析类的时候,会有更多深入的看法。而且类的使用百家争鸣,有人用非常酷炫的方式实现一些功能,但对于那种中规中矩的团队,可能就不能接受了,这无关对错,纯粹是风格的差异。编程不是一成不变的工程化,更像是计算机时代的艺术创作。
后视镜 20191113
网友评论