python类和对象

Python 类与对象基础 + 函数编写详解

一、类与对象的基本概念
  1. 类 (Class)

    • 蓝图/模板,描述一类事物的 属性行为

    • 例如:汽车类 可以有属性(颜色、品牌)和方法(启动、刹车)

  2. 对象 (Object)

    • 类的具体实例

    • 例如:我的车 = 汽车类("红色", "特斯拉")

二、类的定义与使用
1. 基本语法
class 类名:
    def __init__(self, 参数1, 参数2):  # 构造函数
        self.属性1 = 参数1
        self.属性2 = 参数2
    
    def 方法名(self, 参数):  # 实例方法
        # 方法体
        return 结果
2. 示例代码
#!/usr/bin/python3
 
#类定义
class people:
    #定义基本属性
    name = ''
    age = 0
    #定义私有属性,私有属性在类外部无法直接进行访问
    __weight = 0
    #定义构造方法
    def __init__(self,n,a,w):
        self.name = n
        self.age = a
        self.__weight = w
    def speak(self):
        print("%s 说: 我 %d 岁。" %(self.name,self.age))
 
# 实例化类
p = people('runoob',10,30)
p.speak()
​
#runoob 说: 我 10 岁。
3. 关键点
  • self:代表类的实例,必须作为方法的第一个参数

  • __init__:构造函数,在创建对象时自动调用

  • 属性访问对象名.属性(如 my_dog.name

  • 方法调用对象名.方法()(如 my_dog.bark()

三、函数的定义与使用
1. 基本语法
def 函数名(参数1, 参数2=默认值):
    """函数文档字符串(可选)"""
    # 函数体
    return 返回值  # 可选
2. 示例代码
def add(a, b=1):  # b有默认值
    """计算两数之和"""
    return a + b
​
result = add(3, 5)  # 调用
print(result)  # 输出:8
3. 函数类型
类型示例特点
无参函数def greet():直接调用 greet()
有参函数def add(a, b):需传参 add(3, 5)
默认参数def log(msg, level=1):log("Error")log("Error", 2)
返回函数def sqrt(x): return x**0.5必须用 result = sqrt(4) 接收返回值
四、类 vs 函数的区别
特性类 (Class)函数 (Function)
目的封装数据+行为执行特定任务
调用方式先实例化对象,再调方法直接调用
状态保存通过属性维护状态无状态(除非用全局变量)
示例car.drive()calculate_sum(3, 5)
五、实际应用场景
  1. 适合用类的情况

    • 需要维护状态(如游戏角色、银行账户)

    • 多个方法操作同一组数据

  2. 适合用函数的情况

    • 独立功能(如数学计算、字符串处理)

    • 无状态操作(如 len(), max()

继承魔法方法(如__str__

一、继承(Inheritance)

1. 基本概念
  • 继承:子类(派生类 DerivedClassName)会继承父类(基类 BaseClassName)的属性和方法。

  • 语法class 子类名(父类名):

  • 作用:代码复用 + 逻辑分层

2. 示例代码
单继承
class Animal:  # 父类
    def __init__(self, name):
        self.name = name
    
    def speak(self):
        print(f"{self.name} 发出声音")
​
class Dog(Animal):  # 子类继承Animal
    def __init__(self, name, breed):
        super().__init__(name)  # 调用父类的__init__
        self.breed = breed  # 子类新增属性
    
    # 重写父类方法
    def speak(self):
        print(f"{self.name}(品种:{self.breed})汪汪叫!")
​
# 使用
dog = Dog("阿黄", "金毛")
dog.speak()  # 输出:阿黄(品种:金毛)汪汪叫!
​
​
​
​
#!/usr/bin/python3
#类定义
class people:
    #定义基本属性
    name = ''
    age = 0
    #定义私有属性,私有属性在类外部无法直接进行访问
    __weight = 0
    #定义构造方法
    def __init__(self,n,a,w):
        self.name = n
        self.age = a
        self.__weight = w
    def speak(self):
        print("%s 说: 我 %d 岁。" %(self.name,self.age))
 
#单继承示例
class student(people):
    grade = ''
    def __init__(self,n,a,w,g):
        #调用父类的构函
        people.__init__(self,n,a,w)
        self.grade = g
    #覆写父类的方法
    def speak(self):
        print("%s 说: 我 %d 岁了,我在读 %d 年级"%(self.name,self.age,self.grade))
 
 
 
s = student('ken',10,60,3)
s.speak()#ken 说: 我 10 岁了,我在读 3 年级
多继承
#!/usr/bin/python3
 
#类定义
class people:
    #定义基本属性
    name = ''
    age = 0
    #定义私有属性,私有属性在类外部无法直接进行访问
    __weight = 0
    #定义构造方法
    def __init__(self,n,a,w):
        self.name = n
        self.age = a
        self.__weight = w
    def speak(self):
        print("%s 说: 我 %d 岁。" %(self.name,self.age))
 
#单继承示例
class student(people):
    grade = ''
    def __init__(self,n,a,w,g):
        #调用父类的构函
        people.__init__(self,n,a,w)
        self.grade = g
    #覆写父类的方法
    def speak(self):
        print("%s 说: 我 %d 岁了,我在读 %d 年级"%(self.name,self.age,self.grade))
 
#另一个类,多继承之前的准备
class speaker():
    topic = ''
    name = ''
    def __init__(self,n,t):
        self.name = n
        self.topic = t
    def speak(self):
        print("我叫 %s,我是一个演说家,我演讲的主题是 %s"%(self.name,self.topic))
 
#多继承
class sample(speaker,student):
    a =''
    def __init__(self,n,a,w,g,t):
        student.__init__(self,n,a,w,g)
        speaker.__init__(self,n,t)
 
test = sample("Tim",25,80,4,"Python")
test.speak()   #方法名同,默认调用的是在括号中参数位置排前父类的方法 
#我叫 Tim,我是一个演说家,我演讲的主题是 Python
3. 关键点
  • super():调用父类的方法(如 super().__init__(), super(Child,c).myMethod()

  • 方法重写:子类可覆盖父类的方法(如 speak()

    #!/usr/bin/python3
    ​
    class Parent:        # 定义父类
       def myMethod(self):
          print ('调用父类方法')
    ​
    class Child(Parent): # 定义子类
       def myMethod(self):
          print ('调用子类方法')
    ​
    c = Child()          # 子类实例
    c.myMethod()         # 子类调用重写方法
    super(Child,c).myMethod() #用子类对象调用父类已被覆盖的方法
    结果:
    调用子类方法
    调用父类方法

  • 多继承class Child(Parent1, Parent2):(谨慎使用) #方法名同,默认调用的是在括号中参数位置排前父类的方法

4. 类属性与方法
类的私有属性

__private_attrs:两个下划线开头,声明该属性为私有,不能在类的外部被使用或直接访问。在类内部的方法中使用时 self.__private_attrs

#!/usr/bin/python3
 
class JustCounter:
    __secretCount = 0  # 私有变量
    publicCount = 0    # 公开变量
 
    def count(self):
        self.__secretCount += 1
        self.publicCount += 1
        print (self.__secretCount)
 
counter = JustCounter()
counter.count()
counter.count()
print (counter.publicCount)
print (counter.__secretCount)  # 报错,实例不能访问私有变量
​
​
1
2
2
Traceback (most recent call last):
  File "test.py", line 16, in <module>
    print (counter.__secretCount)  # 报错,实例不能访问私有变量
AttributeError: 'JustCounter' object has no attribute '__secretCount'
​
类的方法

在类的内部,使用 def 关键字来定义一个方法,与一般函数定义不同,类方法必须包含参数 self,且为第一个参数,self 代表的是类的实例。

self 的名字并不是规定死的,也可以使用 this,但是最好还是按照约定使用 self

类的私有方法

__private_method:两个下划线开头,声明该方法为私有方法,只能在类的内部调用 ,不能在类的外部调用。self.__private_methods

#!/usr/bin/python3
 
class Site:
    def __init__(self, name, url):
        self.name = name       # public
        self.__url = url   # private
 
    def who(self):
        print('name  : ', self.name)
        print('url : ', self.__url)
 
    def __foo(self):          # 私有方法
        print('这是私有方法')
 
    def foo(self):            # 公共方法
        print('这是公共方法')
        self.__foo()
 
x = Site('菜鸟教程', 'www.runoob.com')
x.who()        # 正常输出
x.foo()        # 正常输出
x.__foo()      # 报错
类的专有方法:
  • init : 构造函数,在生成对象时调用

  • del : 析构函数,释放对象时使用

  • repr : 打印,转换

  • setitem : 按照索引赋值

  • getitem: 按照索引获取值

  • len: 获得长度

  • cmp: 比较运算

  • call: 函数调用

  • add: 加运算

  • sub: 减运算

  • mul: 乘运算

  • truediv: 除运算

  • mod: 求余运算

  • pow: 乘方

运算符重载

Python同样支持运算符重载,我们可以对类的专有方法进行重载

#!/usr/bin/python3
 
class Vector:
   def __init__(self, a, b):
      self.a = a
      self.b = b
 
   def __str__(self):
      return 'Vector (%d, %d)' % (self.a, self.b)
   
   def __add__(self,other):
      return Vector(self.a + other.a, self.b + other.b)
 
v1 = Vector(2,10)
v2 = Vector(5,-2)
print (v1 + v2)

二、魔法方法(Magic Methods)

1. 基本概念
  • 以双下划线 __ 包围的特殊方法(如 __str__

  • Python 自动调用它们实现特定功能(如打印对象、运算符重载)

2. 常用魔法方法
方法名作用触发场景
__init__构造函数obj = Class()
__str__定义对象的打印格式print(obj)
__len__定义对象的长度len(obj)
__add__重载 + 运算符obj1 + obj2
3. 示例代码
class Book:
    def __init__(self, title, author):
        self.title = title
        self.author = author
    
    # 控制打印对象的输出
    def __str__(self):
        return f"《{self.title}》 - {self.author}"
    
    # 重载 + 运算符(合并书名)
    def __add__(self, other):
        return Book(f"{self.title}&{other.title}", "合辑")
​
# 使用
book1 = Book("Python入门", "张三")
book2 = Book("算法导论", "李四")
print(book1)          # 输出:《Python入门》 - 张三
combined = book1 + book2
print(combined)       # 输出:《Python入门&算法导论》 - 合辑
4. 为什么用 __str__
  • 直接打印对象时,默认输出 <__main__.Book object at 0x...>

  • 定义 __str__ 后,print(obj) 会输出更友好的信息


三、继承 + 魔法方法综合案例

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def __str__(self):
        return f"{self.name} ({self.age}岁)"
​
class Student(Person):
    def __init__(self, name, age, student_id):
        super().__init__(name, age)
        self.student_id = student_id
    
    # 重写__str__,追加学号信息
    def __str__(self):
        return f"{super().__str__()} | 学号:{self.student_id}"
​
# 使用
stu = Student("小明", 18, "S12345")
print(stu)  # 输出:小明 (18岁) | 学号:S12345

四、练习题

  1. 继承练习

    • 创建 Shape 父类(属性:颜色;方法:area()

    • 子类 CircleRectangle 分别实现自己的 area() 计算逻辑

  2. 魔法方法练习

    • Circle 类添加 __eq__ 方法,实现两个圆比较半径是否相等

    • 示例:print(circle1 == circle2)


五、总结

技术核心要点典型应用场景
继承super() 调用父类,方法重写代码复用,扩展功能
魔法方法双下划线命名,自动触发(如 __str__定制对象行为/运算符

六、 扩展思考

如果父类的 __str__ 不存在怎么办?

  • Python 会调用 object.__str__(默认返回类名和内存地址)

  • 因此建议所有基类都明确定义 __str__

命名空间

  • 内置名称(built-in names), Python 语言内置的名称,比如函数名 abs、char 和异常名称 BaseException、Exception 等等。

  • 全局名称(global names),模块中定义的名称,记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量。

  • 局部名称(local names),函数中定义的名称,记录了函数的变量,包括函数的参数和局部定义的变量。(类中定义的也是)

命名空间查找顺序:

假设我们要使用变量 runoob,则 Python 的查找顺序为:局部的命名空间 -> 全局命名空间 -> 内置命名空间

# var1 是全局名称
var1 = 5
def some_func(): 
  
    # var2 是局部名称
    var2 = 6
    def some_inner_func(): 
  
        # var3 是内嵌的局部名称
        var3 = 7

作用域

作用域就是一个 Python 程序可以直接访问命名空间的正文区域。

在一个 python 程序中,直接访问一个变量,会从内到外依次访问所有的作用域直到找到,否则会报未定义的错误。

Python 中,程序的变量并不是在哪个位置都可以访问的,访问权限决定于这个变量是在哪里赋值的。

变量的作用域决定了在哪一部分程序可以访问哪个特定的变量名称。

Python 的作用域一共有 4 种,分别是:

有四种作用域:

  • L(Local):最内层,包含局部变量,比如一个函数/方法内部。

  • E(Enclosing):包含了非局部(non-local)也非全局(non-global)的变量。比如两个嵌套函数,一个函数(或类) A 里面又包含了一个函数 B ,那么对于 B 中的名称来说 A 中的作用域就为 nonlocal。

  • G(Global):当前脚本的最外层,比如当前模块的全局变量。

  • B(Built-in): 包含了内建的变量/关键字等,最后被搜索。

LEGB 规则(Local, Enclosing, Global, Built-in):Python 查找变量时的顺序是: L –> E –> G –> B

  1. Local:当前函数的局部作用域。

  2. Enclosing:包含当前函数的外部函数的作用域(如果有嵌套函数)。

  3. Global:当前模块的全局作用域。

  4. Built-in:Python 内置的作用域。

在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内置中找。

img

g_count = 0  # 全局作用域global
def outer():
    o_count = 1  # 闭包函数外的函数中,
    def inner():
        i_count = 2  # 局部作用域local
​

Python 中只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域,其它的代码块(如 if/elif/else/、try/except、for/while等)是不会引入新的作用域的,也就是说这些语句内定义的变量,外部也可以访问。

全局变量和局部变量

定义在函数内部的变量拥有一个局部作用域,定义在函数外的拥有全局作用域。

局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问。

在函数内部声明的变量只在函数内部的作用域中有效,调用函数时,这些内部变量会被加入到函数内部的作用域中,并且不会影响到函数外部的同名变量.

局部变量优先级高于全局变量,因此如果局部变量和全局变量同名,函数内部会使用局部变量。

total = 0  # 这是一个全局变量
​
​
# 可写函数说明
def sum(arg1, arg2):
    # 返回2个参数的和."
    total = arg1 + arg2  # total在这里是局部变量.
    print("函数内是局部变量 : ", total)
    return total
​
​
# 调用sum函数
sum(10, 20)
print("函数外是全局变量 : ", total)
​
#函数内是局部变量 :  30
#函数外是全局变量 :  0
​
​
​
def my_function():
    x = 5  # 局部变量
    print(x)  # 访问局部变量 x
​
my_function()  # 输出 5
print(x)  # 报错: NameError: name 'x' is not defined

global 和 nonlocal关键字

内部作用域想修改外部作用域的变量时,就要用到 global 和 nonlocal 关键字了。

以下实例修改全局变量 num:

#!/usr/bin/python3
 
num = 1
def fun1():
    global num  # 需要使用 global 关键字声明,内部想修改外部
    print(num) 
    num = 123
    print(num)
fun1()
print(num)
​
​
#1
#123
#123

如果要修改嵌套作用域(enclosing 作用域,外层非全局作用域)中的变量则需要 nonlocal 关键字了

#!/usr/bin/python3
 
def outer():
    num = 10
    def inner():
        nonlocal num   # nonlocal关键字声明
        num = 100
        print(num)
    inner()
    print(num)
outer()
​
#100
#100

特殊情况:

#!/usr/bin/python3
 
a = 10
def test():
    a = a + 1
    print(a)
test()
​
#以上程序执行,报错信息如下
Traceback (most recent call last):
  File "test.py", line 7, in <module>
    test()
  File "test.py", line 5, in test
    a = a + 1
UnboundLocalError: local variable 'a' referenced before assignment
#错误信息为局部作用域引用错误,因为 test 函数中的 a 使用的是局部,未定义,无法修改。
​
​
#修改 a 为全局变量:
#!/usr/bin/python3
 
a = 10
def test():
    global a
    a = a + 1
    print(a)
test()
​
#11
​
#也可以通过函数参数传递:
​
#!/usr/bin/python3
 
a = 10
def test(a):
    a = a + 1
    print(a)
test(a)
​
#11

总结

  • 全局变量在函数外部定义,可以在整个文件中访问。

  • 局部变量在函数内部定义,只能在函数内访问。

  • 使用 global 可以在函数中修改全局变量。

  • 使用 nonlocal 可以在嵌套函数中修改外部函数的变量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值