18.Python中的导入类练习题

Python 导入类练习题(含答案)

1. 创建模块并用 import 方式导入

题目: 创建两个文件:

  • dog.py:定义一个 Dog 类,有属性 name(名字)和 breed(品种),有方法 bark() 打印”xx(品种)的 xx 说:汪汪!”
  • main.py:用 import dog 方式导入,创建一个 Dog 实例并调用 bark()

参考答案:

文件:dog.py

"""狗类模块"""

class Dog:
    def __init__(self, name, breed):
        self.name  = name
        self.breed = breed

    def bark(self):
        print(f"{self.breed}的 {self.name} 说:汪汪!")

if __name__ == "__main__":
    d = Dog("旺财", "金毛")
    d.bark()

文件:main.py

import dog   # 导入整个模块

my_dog = dog.Dog("旺财", "金毛")   # 使用时加模块名前缀
my_dog.bark()
# 输出:金毛的 旺财 说:汪汪!

关键点: import dog 导入整个模块,使用类时必须写 dog.Dog(...),模块名作为前缀。


2. 用 from…import 方式导入

题目: 继续使用第 1 题的 dog.py,在新的 main2.py 中用 from dog import Dog 方式导入,创建实例并调用 bark()。比较和第 1 题写法的区别。

参考答案:

文件:main2.py

from dog import Dog   # 只导入 Dog 这个类

my_dog = Dog("咪咪", "柴犬")   # 直接用类名,不需要 dog. 前缀
my_dog.bark()
# 输出:柴犬的 咪咪 说:汪汪!

两种方式的区别:

写法 使用方式 特点
import dog dog.Dog(...) 能看出类来自哪个模块
from dog import Dog Dog(...) 写法更简洁,但看不出来源

3. 从一个模块导入多个类

题目: 创建 shapes.py,里面定义两个类:

  • Circle:属性 radius(半径),方法 area() 返回面积(3.14159 * radius ** 2
  • Rectangle:属性 widthheight,方法 area() 返回面积

main.py 中一行导入两个类,各创建一个实例,打印面积。

参考答案:

文件:shapes.py

"""几何图形类"""

class Circle:
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return 3.14159 * self.radius ** 2

class Rectangle:
    def __init__(self, width, height):
        self.width  = width
        self.height = height

    def area(self):
        return self.width * self.height

if __name__ == "__main__":
    c = Circle(5)
    r = Rectangle(4, 6)
    print(f"圆面积:{c.area():.2f}")
    print(f"矩形面积:{r.area():.2f}")

文件:main.py

from shapes import Circle, Rectangle   # 一行导入两个类,用逗号分隔

c = Circle(5)
r = Rectangle(4, 6)

print(f"圆的面积:{c.area():.2f}")        # 圆的面积:78.54
print(f"矩形的面积:{r.area():.2f}")      # 矩形的面积:24.00

关键点: from 模块 import 类1, 类2 用逗号分隔,一次导入多个类。


4. 使用别名(as)

题目: 继续使用 shapes.py,在 main.py 中:

  1. from shapes import Rectangle as RectRectangle 起别名 Rect
  2. import shapes as sh 给整个模块起别名,再用 sh.Circle 创建实例

参考答案:

文件:main.py

# 给类起别名
from shapes import Rectangle as Rect

r = Rect(3, 8)
print(f"矩形面积:{r.area()}")   # 矩形面积:24

# 给模块起别名
import shapes as sh

c = sh.Circle(7)
print(f"圆的面积:{c.area():.2f}")   # 圆的面积:153.94

什么时候用别名?

  • 类名很长,写起来麻烦
  • 两个模块里有同名类,需要区分(如 from moduleA import Car as CarA

5. if __name__ == "__main__" 的作用

题目: 有如下 greet.py 文件,里面有一行打印代码放在类外面(模拟”忘记用 if name“的情况)。观察导入时的问题,然后修复它。

有问题的 greet.py

class Greeter:
    def __init__(self, name):
        self.name = name

    def say_hello(self):
        print(f"你好,我是 {self.name}!")

# 这行在被导入时也会执行!
print("greet 模块被加载了,正在测试...")
g = Greeter("测试")
g.say_hello()

main.py

from greet import Greeter

g = Greeter("张三")
g.say_hello()

问题: 运行 main.py 时会额外打印什么?为什么?如何修复?

参考答案:

运行 main.py 的输出:

greet 模块被加载了,正在测试...    ← 导入时自动执行了,不想要这行!
你好,我是 测试!                  ← 导入时自动执行了,不想要这行!
你好,我是 张三!                  ← 这才是我们想要的

修复后的 greet.py

class Greeter:
    def __init__(self, name):
        self.name = name

    def say_hello(self):
        print(f"你好,我是 {self.name}!")

# 测试代码放在这里,只有直接运行 greet.py 时才执行,被导入时不执行
if __name__ == "__main__":
    print("greet 模块被加载了,正在测试...")
    g = Greeter("测试")
    g.say_hello()

修复后运行 main.py 的输出:

你好,我是 张三!   ← 只有我们想要的内容

关键点: 模块里的测试代码必须放在 if __name__ == "__main__": 里面,否则被导入时会自动执行。


6. 跨文件继承(子类和父类在不同文件)

题目:

  • animal.py:定义父类 Animal,有 name 属性和 breathe() 方法
  • dog.py:从 animal.py 导入 Animal,定义子类 Dog,新增 breed 属性和 bark() 方法
  • main.py:从 dog.py 导入 Dog,创建实例,调用 breathe()bark()

参考答案:

文件:animal.py

"""动物基类"""

class Animal:
    def __init__(self, name):
        self.name = name

    def breathe(self):
        print(f"{self.name} 在呼吸")

if __name__ == "__main__":
    a = Animal("动物")
    a.breathe()

文件:dog.py

"""狗类,继承自 Animal"""

from animal import Animal   # 导入父类

class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name)   # 调用父类 __init__
        self.breed = breed

    def bark(self):
        print(f"{self.breed}的 {self.name} 说:汪汪!")

if __name__ == "__main__":
    d = Dog("旺财", "金毛")
    d.breathe()
    d.bark()

文件:main.py

from dog import Dog   # 只需导入 Dog,它内部已经处理了 Animal 的导入

my_dog = Dog("旺财", "拉布拉多")
my_dog.breathe()   # 旺财 在呼吸(继承自 Animal)
my_dog.bark()      # 拉布拉多的 旺财 说:汪汪!

关键点: main.py 只导入 Dog,不需要显式导入 Animal,因为 dog.py 里已经处理了继承关系。


7. 创建并使用包

题目: 创建以下文件结构:

练习7/
├── tools/
│   ├── __init__.py    (空文件)
│   ├── calculator.py  (含 Calculator 类)
└── main.py

Calculator 类有方法:

  • add(a, b) 返回 a + b
  • subtract(a, b) 返回 a – b
  • multiply(a, b) 返回 a * b

main.py 中用 from tools.calculator import Calculator 方式导入并测试。

参考答案:

文件:tools/__init__.py

# 空文件,让 Python 把 tools 文件夹识别为包

文件:tools/calculator.py

"""计算器类"""

class Calculator:
    def add(self, a, b):
        return a + b

    def subtract(self, a, b):
        return a - b

    def multiply(self, a, b):
        return a * b

if __name__ == "__main__":
    calc = Calculator()
    print(calc.add(3, 4))        # 7
    print(calc.subtract(10, 3))  # 7
    print(calc.multiply(4, 5))   # 20

文件:main.py

from tools.calculator import Calculator   # 包名.模块名.类名

calc = Calculator()

print(calc.add(10, 5))        # 15
print(calc.subtract(10, 5))   # 5
print(calc.multiply(10, 5))   # 50

关键点: 从包中导入时,用 from 包名.模块名 import 类名 的格式。


8. 在 __init__.py 中统一导出

题目: 在第 7 题基础上,给 tools/ 包再添加一个 converter.py

tools/
├── __init__.py       (需要修改)
├── calculator.py
└── converter.py      (新增)

Converter 类有方法:

  • celsius_to_fahrenheit(c) 摄氏转华氏:c * 9/5 + 32
  • kg_to_pound(kg) 千克转磅:kg * 2.20462

要求:__init__.py 里统一导出 CalculatorConverter,让 main.py 能用 from tools import Calculator, Converter 直接导入。

参考答案:

文件:tools/converter.py

"""单位换算类"""

class Converter:
    def celsius_to_fahrenheit(self, c):
        return c * 9 / 5 + 32

    def kg_to_pound(self, kg):
        return kg * 2.20462

if __name__ == "__main__":
    conv = Converter()
    print(conv.celsius_to_fahrenheit(100))   # 212.0
    print(conv.kg_to_pound(70))              # 154.3234

文件:tools/__init__.py

from .calculator import Calculator   # 相对导入
from .converter import Converter

文件:main.py

from tools import Calculator, Converter   # 不需要指定子模块名了!

calc = Calculator()
conv = Converter()

print(calc.multiply(6, 7))                    # 42
print(conv.celsius_to_fahrenheit(37))         # 98.6(体温)
print(f"{conv.kg_to_pound(60):.2f} 磅")      # 132.28 磅

关键点: __init__.py 中的 from .模块名 import 类名(相对导入)让包的用户可以更简洁地导入,. 代表当前包目录。


9. 避免文件名与标准库冲突

题目: 下面的代码运行后报错,分析原因并修复:

项目目录/
├── random.py     ← 自己写的文件
└── main.py

random.py(自己写的):

class RandomHelper:
    def pick(self, items):
        return items[0]

main.py

import random

numbers = [1, 2, 3, 4, 5]
print(random.choice(numbers))   # 想用标准库 random 的 choice 函数

报错: AttributeError: module 'random' has no attribute 'choice'

参考答案:

原因: 当前目录下有一个叫 random.py 的文件,Python 导入时优先找当前目录,导入的是你自己写的 random.py(里面只有 RandomHelper 类,没有 choice 函数),而不是标准库里的 random 模块。

修复方案: 把自己的文件改名,不要和标准库重名:

项目目录/
├── random_helper.py   ← 改名,避免冲突
└── main.py

random_helper.py

class RandomHelper:
    def pick(self, items):
        return items[0]

main.py(修复后):

import random   # 现在导入的是标准库的 random

numbers = [1, 2, 3, 4, 5]
print(random.choice(numbers))   # 正常输出,如:3

常见的”雷区”文件名(绝对不要用):

禁止使用的文件名 对应的标准库
random.py random(随机数)
os.py os(操作系统接口)
math.py math(数学函数)
string.py string(字符串工具)
time.py time(时间模块)
json.py json(JSON处理)

10. 找错题:找出并修复所有错误

下面有三处错误,找出来并修复:

文件:phone.py

class Phone:
    def __init__(self, brand, model):
        self.brand = brand
        self.model = model

    def info(self):
        return f"{self.brand} {self.model}"

print("加载完毕")   # 错误1

文件:smartphone.py

class Smartphone(Phone):   # 错误2:没有导入 Phone
    def __init__(self, brand, model, os):
        super().__init__(brand, model)
        self.os = os

    def info(self):
        return super().info() + f"({self.os})"

文件:main.py

from phone import Phone
from smartphone import Smartphone

p  = phone("苹果", "14")       # 错误3:类名写错了
sp = Smartphone("三星", "S23", "Android")

print(p.info())
print(sp.info())

参考答案:

错误 1:phone.py 里的 print("加载完毕") 没有放在 if __name__ == "__main__":

# phone.py 修复版
class Phone:
    def __init__(self, brand, model):
        self.brand = brand
        self.model = model

    def info(self):
        return f"{self.brand} {self.model}"

if __name__ == "__main__":   # 修复:测试代码放在这里
    print("加载完毕")

错误 2:smartphone.py 里使用了 Phone 但没有导入它

# smartphone.py 修复版
from phone import Phone   # 修复:导入父类 Phone

class Smartphone(Phone):
    def __init__(self, brand, model, os):
        super().__init__(brand, model)
        self.os = os

    def info(self):
        return super().info() + f"({self.os})"

错误 3:main.pyphone("苹果", "14") 应该是 Phone("苹果", "14")(类名首字母大写)

# main.py 修复版
from phone import Phone
from smartphone import Smartphone

p  = Phone("苹果", "14")          # 修复:类名首字母大写
sp = Smartphone("三星", "S23", "Android")

print(p.info())    # 苹果 14
print(sp.info())   # 三星 S23(Android)

11. 综合题:用户账号系统

题目: 创建以下文件结构:

账号系统/
├── models/
│   ├── __init__.py
│   ├── user.py       (User 类)
│   └── admin.py      (Admin 类,继承 User)
└── main.py

要求:

  • User:属性 usernameemail,方法 info() 返回用户信息,方法 login() 打印”xx 已登录”
  • Admin(User):新增属性 level(管理级别,整数),覆盖 info() 追加管理级别,新增方法 ban_user(target_name) 打印”管理员 xx 封禁了用户 xx”
  • __init__.py:统一导出两个类
  • main.py:用 from models import User, Admin 导入,创建普通用户和管理员各一个,演示各方法

参考答案:

文件:models/user.py

"""用户类"""

class User:
    def __init__(self, username, email):
        self.username = username
        self.email    = email

    def info(self):
        return f"用户:{self.username},邮箱:{self.email}"

    def login(self):
        print(f"{self.username} 已登录")

if __name__ == "__main__":
    u = User("张三", "zhangsan@example.com")
    print(u.info())
    u.login()

文件:models/admin.py

"""管理员类"""

from .user import User   # 相对导入父类

class Admin(User):
    def __init__(self, username, email, level):
        super().__init__(username, email)
        self.level = level

    def info(self):
        return super().info() + f",管理级别:{self.level}"

    def ban_user(self, target_name):
        print(f"管理员 {self.username} 封禁了用户 {target_name}")

if __name__ == "__main__":
    a = Admin("超级管理员", "admin@example.com", 1)
    print(a.info())
    a.ban_user("捣乱用户")

文件:models/__init__.py

from .user import User
from .admin import Admin

文件:main.py

from models import User, Admin

# 普通用户
user = User("李四", "lisi@example.com")
user.login()
print(user.info())

print()

# 管理员
admin = Admin("王管理", "wang@example.com", 2)
admin.login()
print(admin.info())
admin.ban_user("捣乱用户123")

print()

# isinstance 验证
print(isinstance(admin, Admin))  # True
print(isinstance(admin, User))   # True(Admin 继承 User)

运行结果:

李四 已登录
用户:李四,邮箱:lisi@example.com

王管理 已登录
用户:王管理,邮箱:wang@example.com,管理级别:2
管理员 王管理 封禁了用户 捣乱用户123

True
True

12. 相对导入练习

题目: 下面的包结构中,b.py 需要用到 a.py 里的 ClassA。有两种写法,请判断哪种是相对导入、哪种是绝对导入,并说明分别在什么情况下使用:

mypackage/
├── __init__.py
├── a.py       (定义了 ClassA)
└── b.py       (需要导入 ClassA)

写法一(在 b.py 中):

from mypackage.a import ClassA

写法二(在 b.py 中):

from .a import ClassA

参考答案:

写法 类型 说明
from mypackage.a import ClassA 绝对导入 从项目根目录出发,写完整路径
from .a import ClassA 相对导入 . 代表当前包(mypackage),只写相对路径

使用建议:

  • 包内部文件互相导入时,推荐用相对导入from .a import ClassA
    • 好处:如果整个包被改名,内部的相对导入不需要修改
  • 主程序(包外部)导入时,必须用绝对导入from mypackage.a import ClassA
    • 相对导入在顶级脚本里会报 ImportError

发表评论