6 min read

Python Learning 5 类与模块

from homework_chapter5 import Car

new_car = Car("Subaru")
new_car.set_model("WRX")
new_car.set_year(2014)
new_car.get_info()
---------------------------------------------------------------------------

NameError                                 Traceback (most recent call last)

d:\project\packages\Python\Python类与模块.ipynb Cell 1 in <cell line: 1>()
----> <a href='vscode-notebook-cell:/d%3A/project/packages/Python/Python%E7%B1%BB%E4%B8%8E%E6%A8%A1%E5%9D%97.ipynb#ch0000000?line=0'>1</a> from homework_chapter5 import Car
      <a href='vscode-notebook-cell:/d%3A/project/packages/Python/Python%E7%B1%BB%E4%B8%8E%E6%A8%A1%E5%9D%97.ipynb#ch0000000?line=2'>3</a> new_car = Car("Subaru")
      <a href='vscode-notebook-cell:/d%3A/project/packages/Python/Python%E7%B1%BB%E4%B8%8E%E6%A8%A1%E5%9D%97.ipynb#ch0000000?line=3'>4</a> new_car.set_model("WRX")


File d:\project\packages\Python\homework_chapter5.py:3, in <module>
----> 3 class Car:
      4     brand, model, year
      6     def __init__(self, brand):


File d:\project\packages\Python\homework_chapter5.py:4, in Car()
      3 class Car:
----> 4     brand, model, year
      6     def __init__(self, brand):
      7         self.brand = brand


NameError: name 'brand' is not defined

Python是也是一个面对对象编程(Object-Oriented Programming)的语言,面对对象编程是一种设计思想,意味着我们把对象作为程序的基本单元,而每个对象包含了自己的属性和方法。面向对象编程主要有以下特点:

  • 封装(Encapsulation):对外部世界隐藏对象的工作细节。
  • 继承(Inheritance):继承使子类具有父类的各种属性和方法,而不需要编写相同的代码。
  • 多态(Polymorphism):为不同的数据类型的实体提供统一的接口。

使用OOP有以下的优点:

  • 提高软件开发的生产效率
  • 使软件的可维护性更好
  • 提高软件的质量

在 Python 中,元组、列表和字典等数据类型是对象,函数也是对象。那么,我们能创建自己的对象吗?Of Course!跟其他 OOP 语言类似,我们使用类(class)来自定义对象。

类和实例(Class, Instance)

每个类都有自己的属性(attribute)和方法(method),比如一个人的身高、体重和年龄,这些都是属性,而吃饭、说话和洗澡都是方法。(要注意:在class外部定语的可执行函数叫做function,类内部的函数叫做方法method)

类的定义以class为开头,类名的首字母推荐要大写,冒号之后换行缩进紧跟着属性和方法的定义,属性无非就是一个变量的定义,而方法的定义和函数的定义是一样的,也是以def开头。

请看下面的例子:

# class
class Person:
    # attribute fields
    name = 'William'
    age = 45
    # method
    def greet(self):
        print("Hi, my name is " + self.name)
# Create an Object
p1 = Person()
# Call the method
p1.greet()
Hi, my name is William

类的定义是一个具体实例(instance)的设计蓝图,在创建实例的时候,我们只要调用类名,然后加括号就可以了。在greet方法中,我们使用了特殊参数self,它永远指向创建的实例本身,所以self.name就会指向当前被创建实例的name属性。p1.greet()是方法调用的示范,我们只要在实例名后加上句号(.)紧跟着方法名,即可调用实例的方法。

我们也可以在创建好实例之后,对它的属性和方法进行修改:

# Modify Object Properties
p1.age = 40

# Delete Object Properties
del p1.age

# Delete Objects
del p1

** init 是 Python 中的特殊方法(special method),它用于初始化对象。**它是一个实例被创建时最先被调用的函数,并且每次创建实例,它的__init__都会被调用,而且它的第一个参数永远是 self,指向创建的实例本身。(init是initial的简写,顾名思义就是用来初始化的)

class Person:
    def __init__(self):
        self.name = 'Alice'
    def greet(self):
        print("Hi, my name is " + self.name)
p1 = Person()
p1.greet()
Hi, my name is Alice

我们也可以在__init__方法中添加其他参数,这样我们的的初始化能更加灵活和方便,同时在创建实例的时候,需要传入与__init__方法匹配的参数:

  • 注意需要创建对象的时候同时传入其他参数
class Person:
    def __init__(self, init_name):
        self.name = init_name
    def greet(self):
        print("Hi, my name is " + self.name)
p1 = Person("David")
p1.greet()
Hi, my name is David

继承和多态(Inheritance,Polymorphism)

继承

在面向对象编程中,当我们已经创建了一个类,而又想再创建一个与之相似的类,比如添加几个方法,或者修改原来的方法,这时我们不必从头开始,可以从原来的类派生出一个新的类,我们把原来的类称为父类或基类,而派生出的类称为子类,子类继承了父类的所有数据和方法。

class Animal():
    def __init__(self, name):
        self.name = name
    def greet(self):
        print('Hello, I am %s.' % self.name)

创建一个Dog类

class Dog(): def init(self, name): self.name = name def greet(self): print(‘WangWang.., I am %s. ’ % self.name)

class Dog(Animal):
    def greet(self):
        print('WangWang.., I am %s. ' % self.name)
animal = Animal('animal')
animal.greet()
dog = Dog('dog')
dog.greet()
Hello, I am animal.
WangWang.., I am dog. 

多态

多态的概念其实不难理解,它是指对不同类型的参数进行相同的操作,根据对象(或类)类型的不同而表现出不同的行为。**继承可以拿到父类的所有数据和方法,子类可以重写父类的方法,也可以新增自己特有的方法。**有了继承,才有了多态,这样才能实现为不同的数据类型的实体提供统一的接口。

重点:对不同类型的参数进行相同的参数,然后根据对象的不同表现不同的行为

class Animal():
    def __init__(self, name):
        self.name = name
    def greet(self):
        print(f'Hello, I am {self.name}.')

class Dog(Animal):
    def greet(self):
        print(f'WangWang.., I am {self.name}.')

class Cat(Animal):
    def greet(self):
        print(f'MiaoMiao.., I am {self.name}')

def hello(animal):
    animal.greet()

可以看到,cat 和 dog 是两个不同的对象,对它们调用 greet 方法,它们会自动调用实际类型的 greet 方法,作出不同的响应:

dog = Dog('dog')
hello(dog)
cat = Cat('cat');
hello(cat)
WangWang.., I am dog.
MiaoMiao.., I am cat

Iterators

在某些情况下,我们希望实例对象可被用于for…in循环,这时我们需要在类中定义__iter__和__next__方法

  • 其中,__iter()__方法返回迭代器对象本身
  • __next()__方法返回容器的下一个元素,在没有后续元素时会抛出StopIteration异常。(Python 的 for 循环实质上是先通过内置函数 iter() 获得一个迭代器,然后再不断调用 next() 函数实现的。)
class Fib():
    def __init__(self):
        self.a, self.b = 0, 1
    def __iter__(self):
        return self
    def __next__(self):
        self.a, self.b = self.b, self.a + self.b
        return self.a

fib = Fib()
for i in fib:
    if i > 10: 
         break
    print(i)# 1, 1, 2, 3, 5, 8
1
1
2
3
5
8

访问限制 underscore

在某些情况下,我们希望限制用户访问对象的属性或方法,也就是希望它是私有的,对外隐蔽。比如,对于上面的例子,我们希望 name 属性在外部不能被访问,我们可以在属性或方法的名称前面加上两个下划线,即 __,以下是对之前例子的改动:

class Animal():
    def __init__(self, name):
        self.__name = name
    def greet(self):
        print(f'Hello, I am self.__name.')

animal = Animal('a1')
animal.__name # error
---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

d:\project\packages\Python\Python类与模块.ipynb Cell 25 in <cell line: 8>()
      <a href='vscode-notebook-cell:/d%3A/project/packages/Python/Python%E7%B1%BB%E4%B8%8E%E6%A8%A1%E5%9D%97.ipynb#ch0000043?line=4'>5</a>         print(f'Hello, I am self.__name.')
      <a href='vscode-notebook-cell:/d%3A/project/packages/Python/Python%E7%B1%BB%E4%B8%8E%E6%A8%A1%E5%9D%97.ipynb#ch0000043?line=6'>7</a> animal = Animal('a1')
----> <a href='vscode-notebook-cell:/d%3A/project/packages/Python/Python%E7%B1%BB%E4%B8%8E%E6%A8%A1%E5%9D%97.ipynb#ch0000043?line=7'>8</a> animal.__name


AttributeError: 'Animal' object has no attribute '__name'

需要注意的是,

  • 在 Python 中,以双下划线开头,并且以双下划线结尾(即 xxx)的变量是特殊变量,特殊变量是可以直接访问的。所以,不要用 name 这样的变量名。
  • 另外,如果变量名前面只有一个下划线_,表示此变量不要随便访问,虽然它可以直接被访问。

模块调用

有时候一个模块中放不了许多的类,那么我们就要把一些类放入其他的模块中,当我们需要那些类的时候,只需要调用对应模块即可。创建一个模块很简单,只要把代码保存在一个文件中,然后加上后缀.py即可。你可以任意命名文件名,但是必须以.py为后缀。以下我们在animal.py文件中定义了多个不同的类:

# animal.py
class Animal():
    def __init__(self, name):
        self.name = name
    def greet(self):
        print(f'Hello, I am {self.name}.')

class Dog(Animal):
    def greet(self):
        print(f'WangWang.., I am {self.name}.')

class Cat(Animal):
    def greet(self):
        print(f'MiaoMiao.., I am {self.name}')

如果要在其他文件中调用单个类的不同方法,使用import语句即可:

from animal import Animal
animal = Animal('animal')
animal.greet()
---------------------------------------------------------------------------

ModuleNotFoundError                       Traceback (most recent call last)

d:\project\packages\Python\Python类与模块.ipynb Cell 30 in <cell line: 1>()
----> <a href='vscode-notebook-cell:/d%3A/project/packages/Python/Python%E7%B1%BB%E4%B8%8E%E6%A8%A1%E5%9D%97.ipynb#ch0000053?line=0'>1</a> from animal import Animal
      <a href='vscode-notebook-cell:/d%3A/project/packages/Python/Python%E7%B1%BB%E4%B8%8E%E6%A8%A1%E5%9D%97.ipynb#ch0000053?line=1'>2</a> animal = Animal('animal')
      <a href='vscode-notebook-cell:/d%3A/project/packages/Python/Python%E7%B1%BB%E4%B8%8E%E6%A8%A1%E5%9D%97.ipynb#ch0000053?line=2'>3</a> animal.greet()


ModuleNotFoundError: No module named 'animal'

调用多个类:

from animal import Dog, Cat
dog = Dog('duoduo')
dog.greet()
cat = Cat('Kitty')
cat.greet()

其他调用方法:

# importing an entire module
import animal
cat = animal.Cat('Kitty')

# import all classes from a model
from animal import *
cat = Cat('Kitty')

# Using Aliases 
from animal import Cat as C
cat = C('Kitty')