编程语言
首页 > 编程语言> > python 类中的那些小技巧,滚雪球第四季收尾篇

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

作者:互联网

本篇博客为你带来 python 类中的小技巧,学会就能提高效率。

魔法方法 __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()

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

写在后面

第四季滚雪球学 Python 收工啦!

今天是持续写作的第 237 / 365 天。
期待 关注点赞评论收藏

更多精彩

标签:__,name,python,self,滚雪球,Student,print,my,类中
来源: https://blog.csdn.net/hihell/article/details/120642176