Skip to content

面向对象

基本定义

  • 类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
  • **方法:**类中定义的函数。
  • **类变量:**类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
  • **数据成员:**类变量或者实例变量用于处理类及其实例对象的相关的数据。
  • **方法重写:**如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
  • **局部变量:**定义在方法中的变量,只作用于当前实例的类。
  • **实例变量:**在类的声明中,属性是用变量来表示的,这种变量就称为实例变量,实例变量就是一个用 self 修饰的变量。
  • **继承:**即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟"是一个(is-a)"关系(例图,Dog是一个Animal)。
  • **实例化:**创建一个类的实例,类的具体对象。
  • **对象:**通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。

对象定义

py
class Person:
  # 初始化方法
  def __init__(self, name, age):
    self.name = name
    self.age = age

  # 方法,类定义中的函数的第一个参数必须是self,表示实例化对象本身
  def say_hello(self):
    print(self,'self')
    print(f"Hello, my name is {self.name} and I am {self.age} years old.")
  
  # self可以省略,但是不推荐
  def say_hello2():
    print('say_hello2')

# 实例化
person = Person("John", 30)

# 调用对象的方法
person.say_hello()
person.say_hello2()
print(person.name)
print(person.age)

# 输出结果如下:
# Hello, my name is John and I am 30 years old.
# John
# 30

# 定义类的方式有三种:
# class Person:
    # 属性
    # 方法
# class Person():
    # 属性
    # 方法
# class Person(object):
    # 属性
    # 方法

__init__魔术方法

使用__init__方法,在实例化对象的时候。连带其中的参数,会一并传递给__init__函数自动并执行它。

python
class Person:
  # 初始化实例对象属性
  def __init__(self, name, age):
    # 赋予name、age属性给实例对象本身,相当于实例对象的属性
    self.name = name
    self.age = age

  def say_hello(self):
    print(f"Hello, my name is {self.name} and I am {self.age} years old.")

person = Person("John", 30)
person.say_hello()

# 输出结果如下:
# Hello, my name is John and I am 30 years old.

__call__魔术方法

通过调用 calc 实例并传递参数,会触发 __call__ 方法的执行

python
class Adder:
  def __init__(self, x):
    self.x = x

  def __call__(self, y):
    return self.x + y

adder = Adder(10)
print(adder(20))

# 输出结果如下:
# 30

self的用法

  • 写在实例方法里时,第一个形参代表:调用这个方法的那个对象。

  • 解释器在调用 obj.method(...) 时,会自动把 obj 传给这个第一个参数。

  • 不能省:用 实例.方法() 调用的普通方法,必须有第一个参数接收实例(通常叫 self),否则会报 takes 0 positional arguments but 1 was given。

  • 调用时不用写 self:只写 p.greet(),不要写 p.greet(p)(除非你显式用类调用并自己传实例)。

python
class Person:
  def __init__(self, name):
      self.name = name   # 把数据挂在这个实例上
  # 方法,类定义中的函数的第一个参数必须是self,表示实例化对象本身
  def greet(self):
      return f"Hi, {self.name}"  # 通过 self 访问这个实例的属性
p = Person("Ann")
p.greet()   # 等价于 Person.greet(p),p 会传给 greet 的第一个参数

类的继承

面向对象的三个特性:继承、多态、封装。子类只能继承父类公共的部分,私有的不能继承。

继承的基本语法如下:

python
class Animal:
  def eat(self):
    print('')

  def sound(self):
    print('')

class Cat(Animal):
  # 直接继承Animal类的eat和sound方法
  pass

miaomiao = Cat()
miaomiao.eat()
miaomiao.sound()

类的重写

扩展特性,继承让给子类继承父类的所有公共属性和方法,但是如果仅仅是为了继承公共的属性和方法,继承则完全失去了实际的意义了,现实中应该是在继承以后,子类应该有一些自己的属性和方法。那什么叫重写?重写也叫覆盖,就是当子类成员与父类成员的名字相同的时候,从父类继承下来的成员和方法会重新定义。

python
class Animal:
  def eat(self):
    print('')

  def sound(self):
    print('')

class Cat(Animal):
  # 直接继承Animal类的eat和sound方法
  pass

# 继承Animal类
class Dog(Animal):
  # 重写Animal类的eat方法
  def eat(self):
    print('狗吃骨头')

  # 重写Animal类的sound方法
  def sound(self):
    print('狗叫')


wangcai = Dog()
wangcai.eat()
wangcai.sound()

miaomiao = Cat()
miaomiao.eat()
miaomiao.sound()

super方法

调用父类的属性和方法,我们只需要使用super().属性或者super().方法名()就可以完成调用了。这种是在希望继承父类方法和属性的情况下还希望拥有自己的方法和属性的场景使用。

python
class Car(object):
  def __init__(self, brand, model, color):
    self.brand = brand
    self.model = model
    self.color = color
  
  def drive(self):
    print(f'品牌为{self.brand} 型号为{self.model} 颜色为{self.color} 的汽车在行驶')

  def stop(self):
    print(f'品牌为{self.brand} 型号为{self.model} 颜色为{self.color} 的汽车在停止')

class ElectricCar(Car):
  def __init__(self, brand, model, color, battery_capacity):
    # 调用父类Car的__init__方法,初始化父类Car的属性
    super().__init__(brand, model, color)

    # 初始化电动汽车特有的属性
    self.battery_capacity = battery_capacity

  def charge(self):
    print(f'品牌为{self.brand} 型号为{self.model} 颜色为{self.color} 电池容量为{self.battery_capacity} 的电动汽车在充电')


# 燃油车类,继承Car类,但是没有自己的属性,所以不需要super初始化父类Car的属性
class FuelCar(Car):
  # 加油方法
  def fuel(self):
    print(f'品牌为{self.brand} 型号为{self.model} 颜色为{self.color} 的燃油车在加油')

  # 重写父类Car的drive方法,在这里写成下面这样和不写也没区别,等于没做改动
  def drive(self):
    super().drive()


electric_car = ElectricCar('特斯拉', 'Model 3', '红色', 100)
electric_car.charge()

fuel_car = FuelCar('长城', '哈弗H6', '蓝色')
fuel_car.fuel()
fuel_car.drive()
fuel_car.stop()

多继承

通过多重继承,一个子类就可以同时获得多个父类的所有功能。

python
class Bird:
  def __init__(self, name, age):
    self.name = name
    self.age = age

  def eat(self):
    print(f'{self.name} 正在吃')

  def sleep(self):
    print(f'{self.name} 正在睡觉')

class Fly:
  def fly(self):
    print(f'{self.name} 正在飞')

class Sing:
  def sing(self):
    print(f'{self.name} 正在唱歌')

# 继承Bird类和Fly类,多继承
class FlyBird(Bird, Fly):
  pass

# 继承Bird类和Sing类,多继承
class SingBird(Bird, Sing):
  pass

fly_bird = FlyBird('小鸟', 1)
fly_bird.fly()
fly_bird.eat()
fly_bird.sleep()

sing_bird = SingBird('小鸟', 1)
sing_bird.sing()
fly_bird.eat()
sing_bird.sleep()