细分类的组成成员
之前咱们讲过类大致分两块区域
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
装饰器定义的,它不需要 self
或 cls
参数。静态方法不依赖于类或实例的状态,可以像普通函数一样被调用。
静态方法是类中的函数,不需要实例。静态方法主要是用来存放逻辑性的代码,逻辑上属于类,但是和类本身没有关系,也就是说在静态方法中,不会涉及到类中的属性和方法的操作。可以理解为,静态方法是个独立的、单纯的函数,它仅仅托管于某个类的名称空间中,便于使用和维护。
用法:
- 通过类或实例调用。
- 通常用于与类相关但不需要访问类或实例属性的方法。
示例:
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 |
无 |
访问权限 | 访问实例属性和方法 | 访问类属性和方法 | 无法访问类和实例属性 |
调用方式 | 通过实例调用 | 通过类或实例调用 | 通过类或实例调用 |
适用场景 | 实例相关操作 | 与类相关的操作 | 与类无关的操作 |
方法综合案例
需求
- 设计一个
Game
类 - 属性:
- 定义一个 类属性
top_score
记录游戏的 历史最高分 - 定义一个 实例属性
player_name
记录 当前游戏的玩家姓名
- 定义一个 类属性
- 方法:
- 静态方法
show_help
显示游戏帮助信息 - 类方法
show_top_score
显示历史最高分 - 实例方法
start_game
开始当前玩家的游戏
- 静态方法
- 主程序步骤
- 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的子类。