ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

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

2021-10-11 16:01:57  阅读:191  来源: 互联网

标签:__ name python self 滚雪球 Student print my 类中


本篇博客为你带来 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()

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

写在后面

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

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

更多精彩

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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有