写点什么

python 类中的那些小技巧,滚雪球第四季收尾篇

发布于: 刚刚

本篇博客为你带来 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 定义类

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 进行修饰,它不需要设置 selfcls,但可以设置任意其它参数。


普通的实例方法被调用时,使用如下代码:


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()
复制代码


但需要注意的是,静态方法既不能访问实例对象,也不能访问类,它仅仅是属于某个类的名称空间。


发布于: 刚刚阅读数: 2
用户头像

爬虫 100 例作者,蓝桥签约作者,博客专家 2021.02.06 加入

6 年产品经理+教学经验,3 年互联网项目管理经验; 互联网资深爱好者; 沉迷各种技术无法自拔,导致年龄被困在 25 岁; CSDN 爬虫 100 例作者。 个人公众号“梦想橡皮擦”。

评论

发布
暂无评论
python 类中的那些小技巧,滚雪球第四季收尾篇