本篇博客为你带来 python 类中的小技巧,学会就能提高效率。
__str__
和 __repr__
__str__
:当出现将对象转换为字符串时,会调用这个方法。__repr__
:这两个方法需要对比学习,因为其功能十分类似。
class Student(object): def __init__(self, name): self.name = name # 使用 print 打印类 s = Student("橡皮擦") print(s) # <__main__.Student object at 0x0000000000547710>
上述代码使用 print
打印类,发现直接输出了类对象的内存地址。
如果在类中增加魔法方法 __str__
,可以实现定制化的输出。
class Student(object): def __init__(self, name): self.name = name def __str__(self): return "这是一个学生类,你传递的姓名是:" + self.name # 使用 print 打印类 s = Student("橡皮擦") print(s) # 这是一个学生类,你传递的姓名是:橡皮擦
__repr__
与 __str__
实现的效果基本一致,也是在某种情况下将对象转换为字符串。
__repr__
出现的场景可以通过下述步骤测试(忽略异常,实例化时忘记传递参数)。
在控制台中直接调用 s
对象,即可查阅到 __repr__
方法。
如果希望手动控制 __str__
与 __repr__
方法,可以通过 str()
与 repr()
函数来实现。
一般行业中使用 __repr__
方法,实现对开发人员有意义的字符串进行输出。
如果在类中不使用 __str__
方法,仅使用了 __repr__
方法,那程序在运行时,会自动调用 __repr__
方法。
深浅复制其实都是 python 克隆对象中衍生出来的概念,浅复制只复制对象的第一层,深复制复制整个对象树,概念不容易区分,直接查看代码即可。
浅复制
my_list1 = [[1, 2, 3], ["a", "b", "c"]] my_list2 = list(my_list1) print(my_list2) # 浅复制 ,输出 [[1, 2, 3], ['a', 'b', 'c']] my_list1.append([4, 5, 6]) # 给 my_list1 追加元素 print(my_list2) # my_list2 没有受到影响 , 输出 [[1, 2, 3], ['a', 'b', 'c']] # 但由于浅复制仅复制了第一层,如果修改 my_list1 中的元素 my_list1[0].append(666) print(my_list2) # my_list2 中的第一项受到了影响,输出 [[1, 2, 3, 666], ['a', 'b', 'c']]
如果进行深复制,那两个对象会完全独立,深复制使用 copy
模块的 deepcopy()
实现。
from copy import deepcopy my_list1 = [[1, 2, 3], ["a", "b", "c"]] my_list2 = deepcopy(my_list1) print(my_list2) # 深复制 ,输出 [[1, 2, 3], ['a', 'b', 'c']] my_list1.append([4, 5, 6]) # 给 my_list1 追加元素 print(my_list2) # my_list2 没有受到影响 , 输出 [[1, 2, 3], ['a', 'b', 'c']] my_list1[0].append(666) # 深复制仅复制整个对象数,如果修改 my_list1 中的元素 print(my_list2) # my_list2 不会受到影响,输出 [[1, 2, 3], ['a', 'b', 'c']]
copy 模块中的 copy 方法是浅复制。
namedtuple
与普通的元组一样,是不可变数据类型,一般称作具有名称的元组,它其中的元素可以通过唯一的标志符访问,不使用整数索引,也可以用它定义类,具体实现如下:
from collections import namedtuple Student = namedtuple('Student', ["name", "age"])
其中 namedtuple
函数的第一个参数表示 新创建类的名称,第二个参数是 类中的属性名称,可以用列表,也可以用字符串,但不同属性之间需要用空格隔开。
使用 Student
创建一个对象,代码如下所示:
from collections import namedtuple Student = namedtuple('Student', ["name", "age"]) s1 = Student("橡皮擦", 18) print(s1.name) print(s1.age) print(s1) # Student(name='橡皮擦', age=18),可以看到其自动重写了 `__str__` 方法 print(s1.__doc__) # Student(name, age)
由于元组是不可变的,所以对象初始化之后,不可以在进行修改。
s1.name = "擦姐" # 报错:AttributeError: can't set attribute
namedtuple 内部是由 python 类进行实现的,所以其创建的类可以被继承。
from collections import namedtuple Student = namedtuple('Student', ["name", "age"]) class MidStudent(Student): def run(self): print(self.name,"gogogo") s2 = MidStudent("橡皮擦", 18) print(s2) s2.run()
namedtuple 类具有的特殊属性和方法
_fields:获取类字段:
from collections import namedtuple Student = namedtuple('Student', ["name", "age"]) class MidStudent(Student): def run(self): print(self.name,"gogogo") s2 = MidStudent("橡皮擦", 18) print(s2._fields) # 输出 ('name', 'age')
_asdict:将 namedtuple
对象以字典形式返回:
from collections import namedtuple Student = namedtuple('Student', ["name", "age"]) class MidStudent(Student): def run(self): print(self.name,"gogogo") s2 = MidStudent("橡皮擦", 18) print(s2._asdict()) # OrderedDict([('name', '橡皮擦'), ('age', 18)])
_replace:替换元组中的一些属性值,并返回一个浅复制对象
from collections import namedtuple Student = namedtuple('Student', ["name", "age"]) class MidStudent(Student): def run(self): print(self.name,"gogogo") s2 = MidStudent("橡皮擦", 18) print(s2._replace(name="擦姐")) # MidStudent(name='擦姐', age=18)
类变量与实例变量
这两个概念需要对比着进行学习,先说概念:
下面演示一下二者出现的位置。
class Student(): school_name = "实验小学" # 类变量 def __init__(self, name): self.name = name # 实例变量 def run(self): print(self.name, "在跑步") s1 = Student("橡皮擦") s1.age = 18 # 实例变量
上述代码在两个位置使用了 实例变量,在一个位置使用了 类变量,如果想要访问上述变量,使用下述代码:
print(s1.name, s1.age) # 访问实例变量 print(s1.school_name) # 访问类变量 print(Student.school_name) # 访问类变量
使用对象实例或者类名都可以访问到类变量,但是不能通过类名访问实例变量:
# 错误的演示 print(Student.name) # 异常
接下来假设s2 小明转学了,那代码进行下述修改。
s1 = Student("橡皮擦") s2 = Student("小明") s2.school_name = "科技小学" # 小明转学 print(s1.school_name) # 橡皮擦的学校没有变 print(Student.school_name) # 类变量也没有变
此时问题出现了,通过修改 s2
对象的 school_name
,将其进行了重新赋值操作,但是并没有影响到 Student
类的类变量,这与刚才提及的,修改类变量会影响到所有实例 产生了矛盾,原因是,s2.school_name
表示的创建一个 实例变量,只是该实例变量恰好覆盖了类变量。
如果希望小学改名,需要编写如下代码:
class Student(): school_name = "实验小学" # 类变量 def __init__(self, name): self.name = name # 实例变量 def run(self): print(self.name, "在跑步") s1 = Student("橡皮擦") s2 = Student("小明") Student.school_name = "科技小学" print(s1.school_name) print(s2.school_name)
在实际编码过程中,经常会出现创建一个 实例变量,因为与类变量同名的原因,导致覆盖类变量的场景,需要特别注意下。
类方法与实例方法,在增加静态方法
首先创建一个类,这个类包含上述 3 种方法。
class Student(object): # 普通方法,实例方法 def func(self): print("我是实例方法") @classmethod def cls_func(cls): print("我是类方法") @staticmethod def sta_func(): print("我是静态方法")
在编码过程中,最常出现的就是实例方法,该方法必须具备一个 self
参数,用于表示实例对象,如果希望访问类,可以用 self.__class__
实现对类内部状态的修改。
使用装饰器 @classmethod
,可以将一个普通方法转换为类方法,类方法不需要 self
参数,而需要 cls
参数用于指向类自己。
静态方法需要使用装饰器 @staticmethod
进行修饰,它不需要设置 self
和 cls
,但可以设置任意其它参数。
普通的实例方法被调用时,使用如下代码:
s = Student() s.func() # 调用普通方法
上述写法其实也是 python 提供的语法糖,python 自动将对象名 s
替换到了 func
方法的参数 self
位置,如果不使用语法糖,使用下述代码进行实例方法的调用。
s = Student() Student.func(s) # 给 Student 类的 func 方法传递参数 s
类方法的调用,需要使用类名.方法名()
Student.cls_func()
静态方法的调用,可以使用类名.方法名(),也可以使用对象名.方法名()
s = Student() s.sta_func() Student.sta_func()
但需要注意的是,静态方法既不能访问实例对象,也不能访问类,它仅仅是属于某个类的名称空间。
第四季滚雪球学 Python 收工啦!
今天是持续写作的第 237 / 365 天。
期待 关注,点赞、评论、收藏。
更多精彩