细分类的组成成员

之前咱们讲过类大致分两块区域

class A:
    name = '牛老师'

# 第一部分:静态字段(静态变量)部分

    def __init__(self):
        pass
    def func(self):
        pass

# 第二部分:方法部分

每个区域详细划分

class A:

    company_name = '陈松'  # 静态变量(静态字段)
    __iphone = '132333xxxx'  # 私有静态变量(私有静态字段)

    def __init__(self,name,age): #特殊方法
        self.name = name  #对象属性(普通字段)
        self.__age = age  # 私有对象属性(私有普通字段)

    def func1(self):  # 普通方法
        pass

    def __func(self): #私有方法
        print(666)

    @classmethod  # 类方法
    def class_func(cls):
        """ 定义类方法,至少有一个cls参数 """
        print('类方法')

    @staticmethod  #静态方法
    def static_func():
        """ 定义静态方法 ,无默认参数"""
        print('静态方法')

    @property  # 属性
    def prop(self):
        pass

类的私有成员

对于每一个类的成员而言都有两种形式:

  • 公有成员,在任何地方都能访问
  • 私有成员,只有在类的内部才能方法

私有成员和公有成员的访问限制不同:

静态字段(静态属性)

  • 公有静态字段:类可以访问;类内部可以访问;派生类中可以访问
  • 私有静态字段:仅类内部可以访问;

公有成员(Public Members)

公有成员是指没有任何前缀的属性和方法,默认情况下是公开的,可以被类的任何实例及外部代码访问

当你希望某个属性或方法可以被外部自由访问时,可以将其定义为公有成员。

class MyClass:
    def __init__(self, value):
        self.value = value  # 公有成员

    def public_method(self):
        return f'这是类的公有方法, value: {self.value}'

# 使用公有成员
obj = MyClass(10)
print(obj.value)       # 访问公有属性
print(obj.public_method())     # 调用公有方法

特点

  • 可以在类外部直接访问和修改。
  • 适用于需要外部访问的属性和方法。

私有成员(Private Members)

私有成员是指以双下划线(__)开头的属性和方法。Python 在访问这些成员时会进行名称修饰,使得它们在类外部无法直接访问。

当你希望某个属性或方法不被外部直接访问时,可以将其定义为私有成员。

class MyClass:
    def __init__(self, value):
        self.__value = value  # 公有成员

    def __private_method(self):
        return f'这是类的私有方法, value: {self.__value}'

    def get_value(self):
        return self.__value     # 通过公有方法访问私有成员


# 使用私有成员
obj = MyClass(20)
# print(obj.__private_member)  # 会引发 AttributeError
print(obj.get_value())  # 正确访问私有成员

特点

  • 提供了一种保护机制,防止外部代码直接修改类的内部状态。
  • 适用于需要封装和保护的属性和方法。

公有成员与私有成员的比较

特性 公有成员 私有成员
访问权限 可由外部直接访问和修改 不能直接访问,需通过方法访问
前缀 无前缀 双下划线前缀(__
使用场景 适用于需要被外部访问的属性和方法 适用于需要保护和封装的属性和方法
名称修饰 不进行名称修饰 进行名称修饰,防止直接访问

类方法和静态方法

在 Python 中,普通方法、类方法和静态方法是定义在类中的三种不同类型的方法。它们之间的主要区别在于如何使用和访问它们,以及它们与类和实例的关系。下面详细介绍它们的区别和用法。

1. 普通方法

定义: 普通方法是定义在类中的方法,通常用于操作实例的属性。它的第一个参数必须是 self,表示实例本身。

用法

  • 通过实例调用。
  • 可以访问实例属性和其他实例方法。

示例

class MyClass:
    def __init__(self, value):
        self.value = value

    def instance_method(self):
        return f'对象调用获取值: {self.value}'

# 使用
obj = MyClass(10)
print(obj.instance_method())  # 输出: 对象调用获取值: 10

2. 类方法

定义: 类方法是通过 @classmethod 装饰器定义的,它的第一个参数是 cls,表示类本身,而不是类的实例。类方法可以访问类的属性和其他类方法。

用法

  • 通过类或实例调用。
  • 不能直接访问实例属性,但可以访问类属性。

类方法通常用于需要在类层面上操作的场景,比如修改类级别的属性、创建类的实例等。

示例

class MyClass:
    class_variable = 0

    def __init__(self, value):
        self.value = value

    @classmethod
    def class_method(cls):
        return f'类方法获取类的值: {cls.class_variable}'

# 使用
print(MyClass.class_method())  # 输出: 类方法获取类的值: 0

obj = MyClass(10)
print(obj.class_method())       # 也可以通过实例调用

案例:如下场景:

假设我有一个学生类和一个班级类,想要实现的功能为:

  • 执行班级人数增加的操作、获得班级的总人数;
  • 学生类继承自班级类,每实例化一个学生,班级人数都能增加;
  • 最后,我想定义一些学生,获得班级中的总人数。

思考:这个问题用类方法做比较合适,为什么?因为我实例化的是学生,但是如果我从学生这一个实例中获得班级总人数,在逻辑上显然是不合理的。同时,如果想要获得班级总人数,如果生成一个班级的实例也是没有必要的。

class Student:
    __num = 0  # 私有类变量,用于统计学生实例的数量

    def __init__(self, name, age):
        self.name = name
        self.age = age
        Student.addNum()  # 每次实例化时调用 addNum() 类方法

    @classmethod
    def addNum(cls):
        cls.__num += 1  # 类变量 __num 增加 1

    @classmethod
    def getNum(cls):
        return cls.__num  # 返回当前类变量 __num 的值


# 实例化3个对象
Student('陈松', 18)
Student('阿松', 36)
Student('松松', 73)

# 输出类变量 __num 的值
print(Student.getNum())  # 输出: 3

3. 静态方法

定义: 静态方法是通过 @staticmethod 装饰器定义的,它不需要 selfcls 参数。静态方法不依赖于类或实例的状态,可以像普通函数一样被调用。

静态方法是类中的函数,不需要实例。静态方法主要是用来存放逻辑性的代码,逻辑上属于类,但是和类本身没有关系,也就是说在静态方法中,不会涉及到类中的属性和方法的操作。可以理解为,静态方法是个独立的、单纯的函数,它仅仅托管于某个类的名称空间中,便于使用和维护。

用法

  • 通过类或实例调用。
  • 通常用于与类相关但不需要访问类或实例属性的方法。

示例

class MyClass:
    @staticmethod
    def static_method():
        return 'Static method called'

# 使用
print(MyClass.static_method())  # 输出: Static method called

obj = MyClass()
print(obj.static_method())       # 也可以通过实例调用

比如:我想定义一个关于时间操作的类,其中有一个获取当前时间的函数。

import time

class TimeTest(object):
    def __init__(self, hour, minute, second):
        self.hour = hour
        self.minute = minute
        self.second = second

    @staticmethod
    def showTime():
        return time.strftime("%H:%M:%S", time.localtime())


print(TimeTest.showTime())
t = TimeTest(2, 10, 10)
nowTime = t.showTime()
print(nowTime)

4. 双下方法

双下划线方法(也称为魔法方法或特殊方法)是以双下划线开头和结尾的方法。这些方法具有特定的用途,允许你定义类的行为,特别是在使用运算符和内置函数时。双下方法通常用于实现类的特性或行为,以下是一些常见的双下方法及其用途。

对于双下方法具体的内容,我们后面会讲到

总结对比

特性 普通方法 类方法 静态方法
定义方式 不需要装饰器 @classmethod @staticmethod
第一个参数 self cls
访问权限 访问实例属性和方法 访问类属性和方法 无法访问类和实例属性
调用方式 通过实例调用 通过类或实例调用 通过类或实例调用
适用场景 实例相关操作 与类相关的操作 与类无关的操作

方法综合案例

需求

  1. 设计一个 Game
  2. 属性:
    • 定义一个 类属性 top_score 记录游戏的 历史最高分
    • 定义一个 实例属性 player_name 记录 当前游戏的玩家姓名
  3. 方法:
    • 静态方法 show_help 显示游戏帮助信息
    • 类方法 show_top_score 显示历史最高分
    • 实例方法 start_game 开始当前玩家的游戏
  4. 主程序步骤
    • 1) 查看帮助信息
    • 2) 查看历史最高分
    • 3) 创建游戏对象,开始游戏

img

案例小结

  1. 实例方法—— 方法内部需要访问实例属性
    • 实例方法 内部可以使用 类名. 访问类属性
  2. 类方法 —— 方法内部 需要访问 类属性
  3. 静态方法 —— 方法内部,不需要访问 实例属性类属性

提问

如果方法内部 即需要访问 实例属性,又需要访问 类属性,应该定义成什么方法?

答案

  • 应该定义 实例方法
  • 因为,类只有一个,在 实例方法 内部可以使用 类名. 访问类属性
class Game(object):

    # 游戏最高分,类属性
    top_score = 0

    @staticmethod
    def show_help():
        print("帮助信息:让僵尸走进房间")

    @classmethod
    def show_top_score(cls):
        print("游戏最高分是 %d" % cls.top_score)

    def __init__(self, player_name):
        self.player_name = player_name

    def start_game(self):
        print("[%s] 开始游戏..." % self.player_name)

        # 使用类名.修改历史最高分
        Game.top_score = 999

# 1. 查看游戏帮助
Game.show_help()

# 2. 查看游戏最高分
Game.show_top_score()

# 3. 创建游戏对象,开始游戏
game = Game("小明")

game.start_game()

# 4. 游戏结束,查看游戏最高分
Game.show_top_score()

属性(@property

定义与特点

  • 属性(Property)通过 @property 装饰器定义,允许将一个方法表现得像一个属性一样。
  • 允许使用类似访问属性的方式来调用方法,而不需要像函数那样加上括号 ()
  • 可以提供对类的实例变量的控制,比如在读取或设置时加入逻辑。

使用场景

属性通常用于对于实例变量的访问控制,比如在获取或修改变量时加入额外的逻辑验证或计算。

class MyClass:
    def __init__(self, value):
        self._value = value

    # 定义getter方法
    @property
    def value(self):
        return self._value

    # 定义setter方法
    @value.setter
    def value(self, new_value):
        if new_value < 0:
            raise ValueError("Value cannot be negative!")
        self._value = new_value

    @value.deleter
    def value(self):
        del self._value

# 创建实例
obj = MyClass(10)

# 使用属性getter方法
print(obj.value)  # 输出: 10

# 使用属性setter方法
obj.value = 20
print(obj.value)  # 输出: 20

# 使用属性的delete方法
del obj.value

print(obj.value)    # 再次访问的时候就会找不到这个属性


# 设置负值会引发错误
# obj.value = -5  # 抛出 ValueError: Value cannot be negative!

isinstace 与 issubclass

isinstance(a,b):判断a是否是b类(或者b类的派生类)实例化的对象

class A:
    pass

class B(A):
    pass

obj = B()

print(isinstance(obj,B))
print(isinstance(obj,A))

issubclass(a,b): 判断a类是否是b类(或者b的派生类)的派生类

class A:
    pass

class B(A):
    pass

class C(B):
    pass

print(issubclass(B,A))
print(issubclass(C,A))

思考:那么 list str tuple dict等这些类与 Iterble类 的关系是什么?

from collections import Iterable

print(isinstance([1,2,3], list))  # True
print(isinstance([1,2,3], Iterable))  # True
print(issubclass(list,Iterable))  # True

# 由上面的例子可得,这些可迭代的数据类型,list str tuple dict等 都是 Iterable的子类。
学前沿IT,到英格科技!本文发布时间: 2024-10-25 19:41:31

results matching ""

    No results matching ""