之前的文章我们简单介绍了一下 Python 的函数, 本篇文章我们来看一下 Python 中的面向对象.
Python 从设计之初就已经是一门面向对象的语言, 正因为如此, 在 Python 中创建一个类和对象是很容易的.
面向对象技术简介
类(Class): 用来描述具有相同的属性和方法的对象的集合. 它定义了该集合中每个对象所共有的属性和方法. 对象是类的实例.
方法: 类中定义的函数.
类变量: 类变量在整个实例化的对象中是公用的. 类变量定义在类中且在函数体之外. 类变量通常不作为实例变量使用.
数据成员: 类变量或者实例变量用于处理类及其实例对象的相关的数据.
方法重写: 如果从父类继承的方法不能满足子类的需求, 可以对其进行改写, 这个过程叫方法的覆盖(override), 也称为方法的重写.
局部变量: 定义在方法中的变量, 只作用于当前实例的类.
实例变量: 在类的声明中, 属性是用变量来表示的. 这种变量就称为实例变量, 是在类声明的内部但是在类的其他成员方法之外声明的.
继承: 即一个派生类 (derived class) 继承基类 (base class) 的字段和方法. 继承也允许把一个派生类的对象作为一个基类对象对待. 例如, 有这样一个设计: 一个 Dog 类型的对象派生自 Animal 类, 这是模拟 "是一个(is-a)" 关系(例图, Dog 是一个 Animal).
实例化: 创建一个类的实例, 类的具体对象.
对象: 通过类定义的数据结构实例. 对象包括两个数据成员 (类变量和实例变量) 和方法.
和其它编程语言相比, Python 在尽可能不增加新的语法和语义的情况下加入了类机制.
Python 中的类提供了面向对象编程的所有基本功能: 类的继承机制允许多个基类, 派生类可以覆盖基类中的任何方法, 方法中可以调用基类中的同名方法.
对象可以包含任意数量和类型的数据.
Python 中定义类的方法是以 class k 开头, 我们先定义一个类:
- class Cat:
- def eat(self):
- print("猫吃鱼")
- cat = Cat()
- cat.eat() # 猫吃鱼
如上 , 我们定义了一个 Cat 类, 里面定义了一个方法, 我们可以通过 变量 = 类名() 的方式来实例化一个类, 这样我们就可以调用类里面的属性和方法, 所以当我们调用 cat.eat() 时输出打印 Cat 类里面的 eat() 方法.
在上面的 eat() 方法中, 我们默认穿了一个 self 的参数, 它代表该类的实例, 不一定非叫 self , 可以叫 xxx, 只是我们约定俗成叫 self, 跟 *args 和 **kwargs 一个道理, 那该如何理解这个 self 呢, 如下:
- class Cat:
- def eat(self):
- print(self.name+"吃鱼")
- tom = Cat()
- tom.name = "Tom"
- tom.eat() # Tom 吃鱼
- jerry = Cat()
- jerry.name = "Jerry"
- jerry.eat() # Jerry 吃鱼
在上面的代码中, 我们定义了两个 Cat 的实例, 并且在每个实例中都添加了一个 name 属性, 如果我们想要在 eat() 方法中输出各自定义的 name 值, 我们可以通过 self 属性来定义, 因为 self 就表示该类的实例, 所以就会去拿各自实例里面的 name 值.
上面的代码中我们可以实现输出不同的 name 值, 但是需要我们在实例化后自己定义 name, 我们也可以在定义类的时候就将 name 值传入来做:
- class Cat:
- def __init__(self,name):
- self.name = name
- def eat(self):
- print(self.name+"吃鱼")
- tom = Cat("Tom")
- tom.eat() # Tom 吃鱼
- jerry = Cat("Jerry")
- jerry.eat() # Jerry 吃鱼
上面的代码中, 我们在 Cat 类中定义了一个 __init__() 的方法, 该方法是类自带的方法, 第一个参数必须为 self, 我们可以在里面定义自己所需的变量. 如上, 我们在实例化 Cat 类的时候就将 Tom ,Jeery 传入, 然后在 self 形参后面添加形参, 该形参与传入参数的顺序意义对应, 这样我们就可以使用传入的参数了, 定义和使用时需要在前面加 self.
在上面的代码演示中, 我们可以看出当我们实例化一个类之后, 就能调用该类的方法, 这种方法叫公有方法, 还有一种方法叫私有方法, 就是实例化后不能被调用, 只有自己内部可以调用, 如下:
- class Cat:
- def __init__(self,name):
- self.name= name
- def eat(self):
- print(self.name+"吃鱼")
- self.__run()
- def __run(self):
- print("私有方法")
- tom = Cat("Tom")
- tom.eat() # Tom 吃鱼 私有方法
- tom.__run() # AttributeError: 'Cat' object has no attribute '__run'
上面的代码中当我们实例化 tom 后, 可以通过 eat() 方法调用到 __run() 方法, 但是直接调用 __run() 会报错.
不仅私有方法不能被调用, 私有属性也不能, 如下:
- class Cat:
- age = 11
- __height = 120
- def add(self):
- self.age += 1
- self.__height += 1
- tom = Cat()
- tom.add()
- print(tom.age) # 12
- print(tom.__height) # AttributeError: 'Cat' object has no attribute '__height'
接下来我们看一下类中的继承, 在上面的代码中我们定义了一个 Cat 的类, 里面有一个 eat() 方法, 当我们定义一个 Dog 类时, 它也有一个 eat() 方法, 而且 eat() 方法是一样的, 现在 Cat 类有一个 bark() 方法, Dog 类里也有一个 bark() 方法, 但是这两个方法执行的结果不一样, 而且 Dog 类里有一个 swim() 方法, 而 Cat 里没有, 如果我们都分别定义这两个类的话, 代码会很长, 而且如果两个相同的 eat() 方法需要修改时需要修改两个地方, 这时我们可以用继承的方式解决, 如下:
- class Animal:
- def eat(self):
- print("吃吃吃")
- class Cat(Animal):
- def bark(self):
- print("喵喵喵")
- class Dog(Animal):
- def bark(self):
- print("汪汪汪")
- def swim(self):
- print("狗刨式")
- cat = Cat()
- cat.eat() # 吃吃吃
- cat.bark() # 喵喵喵
- cat.swim() # AttributeError: 'Cat' object has no attribute 'swim'
- dog = Dog()
- dog.eat() # 吃吃吃
- dog.bark() # 汪汪汪
- dog.swim() # 狗刨式
我们将 Cat 类和 Dog 类相同的 eat() 方法定义在了 Animal 类里, 然后在创建 Cat 和 Dog 类时添加了 (Animal), 意思是继承 Animal 类, 这样我们在实例化 Cat 和 Dog 类后就能调用 Animal 类里的方法 eat(), 而且还能调用各自实例里的 bark() 方法, 但是如果没有另一个类, 则不能使用该类的方法, 如 cat 调用 dog 的 swim() 方法就会报错.
当然我们可以让 Cat 类也继承 Dog 类, 如下:
- class Animal:
- def eat(self):
- print("吃吃吃")
- # Cat 类要想继承 Dog 类必须写在 Dog 类后面
- # class Cat(Dog,Animal):
- # def bark(self):
- # print("喵喵喵")
- class Dog(Animal):
- def bark(self):
- print("汪汪汪")
- def swim(self):
- print("狗刨式")
- class Cat(Dog,Animal):
- def bark(self):
- print("喵喵喵")
- cat = Cat()
- cat.eat() # 吃吃吃
- cat.bark() # 喵喵喵
- cat.swim() # 狗刨式
- dog = Dog()
- dog.eat() # 吃吃吃
- dog.bark() # 汪汪汪
- dog.swim() # 狗刨式
如上, 我们可以让 Cat 同时继承 Dog 和 Animal 两个类, 但是如若想要继承某类, 必须先创建该类.
我们也可以多重继承, 即让 Animal 类也继承某类, 这样 Cat 和 Dog 如果继承了 Animal 类, 那么也可以使用 Animal 类继承的父类的属性和方法.
只得注意的是私有属性和方法不能被继承, 因为私有属性和方法只能在自己的类中使用.
接下来我们看一下类中方法的重写:
- class Animal:
- def eat(self):
- print("吃吃吃")
- class Cat(Animal):
- pass
- class Dog(Animal):
- def eat(self):
- print("大口大口吃")
- cat = Cat()
- cat.eat() # 吃吃吃
- dog = Dog()
- dog.eat() # 大口大口吃
在上面的代码中, 我们让 Cat 和 Dog 类都继承了 Animal 类, 但是在 Dog 类中, 我们定义了一个和 Animal 类中一样的方法名, 但是执行的结果不一样, 当我们分别调用实例化的 cat 和 dog 的 eat() 方法时, Cat 类由于没有自己的 eat() 方法, 所以向上寻找, 发现继承的 Animal 类中有 eat() 方法, 所以就调用了 Animal 类中的 eat() 方法, 但是 Dog 类中有 eat() 方法, 所以就调用自己的 eat() 方法, 不在向上寻找, 这就是类中方法的重写.
在上面我们说过了类属性和实例方法, 接下来我们来看一下类方法:
- class Cat:
- # 类属性
- age = 10
- # 实例方法
- def __init__(self):
- self.name = "Tom"
- def info(self):
- print(self.name, self.age)
- # 类方法
- @classmethod
- def addAge(cls):
- cls.age = 22
- tom = Cat()
- tom.info() # Tom 10
- Cat.info() # TypeError: info() missing 1 required positional argument: 'self'
- tom.addAge()
- print(tom.age) # 22
- print(Cat.age) # 22
- Cat.addAge()
- print(tom.age) # 22
- print(Cat.age) # 22
如果我们在类里面的方法前面加 @classmethod, 就表明该方法为类方法, 在说类方法前我们再来看一下实例方法.
当我们实例化一个 tom 后, 我们就可以调用 tom 里面的实例方法, 我们之前说过 self 指的是该类的实例化, 所以当我们调用 tom.info() 时能正常调用, 但是 Cat.info() 时则会报错, 因为里面的 self 指向的是实例化的 tom, 而不是 Cat 类, 所以会报错.
在类方法中, 我们同样谣传一个默认的形参, 默认叫 cls, 它指向的是类本身, 而不是该类的实例化, 所以我们可以通过 cls.age=22 来更改类里面的类属性, 而且类方法 addAge() 可以使用实例化的 tom 来调用, 也可以使用 Cat 类本身来调用.
来源: https://www.cnblogs.com/weijiutao/p/11022129.html