美文网首页Python回忆录
基础篇: 10. Python类简介

基础篇: 10. Python类简介

作者: 后视镜 | 来源:发表于2019-11-13 17:24 被阅读0次

对象封装在我上大学的时候是一个热门的话题,那时候大部分的编程习惯都是以面向过程编程,上大学的时候学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

相关文章

网友评论

    本文标题:基础篇: 10. Python类简介

    本文链接:https://www.haomeiwen.com/subject/allkictx.html