专栏首页极客起源Python编程思想(26):成员变量

Python编程思想(26):成员变量

-----------支持作者请转发本文-----------

李宁老师已经在「极客起源」 微信公众号推出《Python编程思想》电子书,囊括了Python的核心技术,以及Python的主要函数库的使用方法。读者可以在「极客起源」 公众号中输入 160442 开始学习。

-----------正文-----------

在类体内定义的变量,默认属于类本身。如果把类当成类命名空间,那么该类变量其实就是定义在类命名空间内的变量。

1. 类变量和实例变量

在类命名空间内定义的变量就属于类变量,Python可以使用类来读取、修改类变量。例如,下面代码定义了一个 Teacher类,并为该类定义了多个类变量。

示例代码:class_var.py

class Teacher :
    name = '李宁'
    salary = 66666.66
    post_code = '12345678'
    def print_info (self):
        # 尝试直接访问类变量
#        print(name) # 报错
        # 通过类来访问类变量
        print(Teacher.name) # 输出 广州
        print(Teacher.post_code) # 输出 510660
# 通过类来访问Address类的类变量
print(Teacher.name)
teacher = Teacher()
teacher.print_info()
# 修改Teacher类的类变量
Teacher.name = '王军'
Teacher.post_code = '87654321'
teacher.print_info()

在这段代码中为Teacher类定义了两个类变量。

对于类变量而言,它们就是属于在类命名空间内定义的变量,因此程序不能直接访问这些变量,程序必须使用类名来调用类变量。不管是在全局范围内还是函数内访问这些类变量,都必须使用类名进行访问。

当程序第1次调用 Teacher对象的print_info()方法输出两个类变量时,将会输出这两个类变量的初始值。接下来程序通过 Teacher类修改了两个类变量的值,因此当程序第2次通过print_info方法输出两个类变量时,将会输出这两个类变量修改之后的值。

运行上面代码,将会看到如下输出结果:

李宁
李宁
12345678
王军
87654321

实际上,Python完全允许使用对象来访问该对象所属类的类变量。看下面的程序:

示例代码:class_instance_access_classvar

class Country:
    # 定义两个类变量
    value1 = '中国'
    value2 = 960
    def print_info (self):
        print('info方法中: ', self.value1)
        print('info方法中: ', self.value2)

country = Country()
print(country.value1) # 中国
print(country.value2) # 960
country.print_info()
    
# 修改Country类的两个类变量
Country.value1 = '美国'
Country.value2 = 1234
# 调用print_info()方法
country.print_info()

这段代码的Country类中定义了两个类变量,接下来程序完全可以使用 Country对象来访问这两个类变量。

在这段代码的Country类的print_info方法中,程序使用self访问 Country类的类变量,此时self代表print_info方法的调用者,也就是 Country对象,因此这是合法的。

在主程序代码区,程序创建了 Country对象,并通过对象调用Country对象的value1和value2类变量,这也是合法的。

实际上,程序通过对象访问类变量,其本质还是通过类名在访问类变量。运行上面程序,将看到如下输出结果:

中国 960 info方法中: 中国 info方法中: 960 info方法中: 美国 info方法中: 1234

由于通过对象访问类变量的本质还是通过类名在访问,因此如果类变量发生了改变,当程序访问这些类变量时也会读到修改之后的值。例如为程序增加如下代码(接前面的代码),修改Country类的两个类变量。

Country.value1='韩国'
Country.value2 = 250
# 调用info()方法
country.print_info()

上面程序修改了Country类的两个类变量,然后通过对象调用print)info实例方法。运行上面代码,将看到如下输出结果。

info方法中:  韩国
info方法中:  250

从上面的输出结果可以看到,通过实例访问类变量的本质依然是通过类名在访问。需要说明的是,Python允许通过对象访问类变量,但如果程序通过对象尝试对类变量赋值,此时性质就变了。Python是动态语言,赋值语句往往意味着定义新变量。因此,如果程序通过对象对类变量赋值,其实不是对“类变量赋值”,而是定义新的实例变量。

看下面的代码:

实例代码:new_class_var.py

class Product:
    # 定义两个类变量
    name = 'iMac'
    price = 11000
    # 定义实例方法
    def buy(self, name, price):
        # 下面赋值语句不是对类变量赋值,而是定义新的实例变量
        self.name = name
        self.price = price

# 创建Product对象
product = Product()
product.buy('iPhone', 8000)
# 访问product的name和price实例变量
print(product.name) # iPhone
print(product.price) # 8000
# 访问Product的name和price类变量
print(Product.name) # iMac
print(Product.price) # 11000

Product.name = '类变量name'
Product.price = '类变量price'
# 访问product的name和price实例变量
print(product.name)
print(product.price)

product.name = '实例变量name'
product.price = '实例变量price'
print(Product.name)
print(Product.price)

在这段代码中通过实例对name和price变量赋值,看上去很像是对类变量赋值,但实际上不是,而是重新定义了两个实例变量(如果第1次调用该方法)。

在这段diamante中调用 Product对象的 buy()方法之后,访问 Product对象的name和price变量。由于该对象本身已有这两个实例变量,因此程序将会输出该对象的实例变量的值。接下来程序通过Product访问它的name和price类变量,此时才是真的访问类变量。

运行上面程序,将看到如下输出结果:

iPhone
8000
iMac
11000
iPhone
8000
类变量name
类变量price

如果程序通过类修改了两个类变量的值,程序中 Product的实例变量的值也不会受到任何影响。例如如下代码。

Product.name = '类变量name'
Product.price = '类变量price'
# 访问product的name和price实例变量
print(product.name)
print(product.price)

运行上面代码,可以看到如下输出结果

iPhone
8000

上面程序开始就修改了Product类中两个类变量的值,但这种修改对 Inventory对象的实例变量没有任何影响。同样,如果程序对一个对象的实例变量进行了修改,这种修改也不会影响类变量和其他对象的实例变量。例如如下代码。

product.name = '实例变量name'
product.price = '实例变量price'
print(Product.name)
print(Product.price)

运行上面代码,将会看到如下输出结果。

类变量name
类变量 price

从上面的输出结果来看,程序输出的依然是之前对类变量所赋的两个值。

2. 使用 property函数定义属性

如果为 Python类定义了getter和setter等访问器方法,可使用 property函数将它们定义成属性(相当于实例变量)。

property函数的语法格式如下:

property(fget=None, fset=None, fdel-None, doc=None)

从上面的语法格式可以看出,在使用 property函数时,可传入4个参数,分别代表 getter方法、 setter方法、del方法和doc,其中doc是一个文档字符串,用于说明该属性。当然,开发者调用 property也可传入0个(既不能读,也不能写的属性)、1个(只读属性)、2个(读写属性)、3个(读写属性,也可删除)和4个(读写属性,也可删除,包含文档说明)参数。

例如,如下程序定义了一个 Rectangle类,该类使用 property函数定义了一个size属性。

示例代码:rectangle.py

class Rectangle:
    # 定义构造方法
    def __init__(self, width, height):
        self.width = width
        self.height = height
    # 定义set_size()函数
    def set_size (self , size):
        self.width, self.height = size
    # 定义getsize()函数
    def get_size (self):
        return self.width, self.height
     # 定义getsize()函数
    def del_size (self):
        self.width, self.height = 0, 0  
    # 使用property定义属性
    size = property(get_size, set_size, del_size, '用于描述矩形大小的属性')
# 访问size属性的说明文档
print(Rectangle.size.__doc__)
# 通过内置的help()函数查看Rectangle.size的说明文档
help(Rectangle.size)
rect = Rectangle(5, 6)
# 访问rect的size属性
print(rect.size) # (5, 6)
# 对rect的size属性赋值
rect.size = 10, 12
# 访问rect的width、height实例变量
print(rect.width) # 10
print(rect.height) # 12
# 删除rect的size属性
del rect.size
# 访问rect的width、height实例变量
print(rect.width) # 0
print(rect.height) # 0

这段代码使用 property函数定义了一个size属性,在定义该属性时一共传入了4个参数,这意味着该属性可读、可写、可删除,也有说明文档。所以,该程序尝试对Rectangle对象的size属性进行读、写、删除操作,其实这种读、写、删除操作分别被委托给 get_size()、 set_size()和 del_size()方法来实现。

运行上面程序,将会看到如下输出结果:

用于描述矩形大小的属性
Help on property:

    用于描述矩形大小的属性

(5, 6)
10
12
0
0

在使用 property函数定义属性时,也可根据需要只传入少量的参数。例如,如下代码使用property函数定义了一个读写属性,该属性不能删除。

class Person :
    def __init__ (self, first, last):
        self.first = first
        self.last = last
    def get_fullname(self):
        return self.first + ',' + self.last
    def set_fullname(self, fullname):
        first_last = fullname.rsplit(',');
        self.first = first_last[0]
        self.last = first_last[1]
    # 使用property()函数定义fullname属性,只传入2个参数
    # 该属性是一个读写属性,但不能删除
    fullname = property(get_fullname, set_fullname)
p = Person('子龙', '赵')
# 访问fullname属性
print(p.fullname)
# 对fullname属性赋值
p.fullname = '云长,关'
print(p.first)
print(p.last)

在这段代码中使用 property函数定义了 fullname属性,该程序使用 property()函数时只传入两个参数,分别作为 getter和 setter方法,因此该属性是一个读写属性,不能删除。

运行这段代码,将看到如下输出结果:

子龙,赵
云长
关

本文分享自微信公众号 - 极客起源(geekculture),作者:geekori

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-06-25

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Python编程思想(30):用 metaclass搞定一批类的特性

    如果希望创建某一批类全部具有某种特征,则可以通过 metaclass来实现。使用 metaclass可以在创建类时动态修改类定义。为了使用 metaclass动...

    蒙娜丽宁
  • Python编程思想(4):字符串类型

    字符串的意思就是“一串字符”,也可以看做是字符的序列。比如“Helo, Mike”是一个字符串,“How are you?”也是一个字符串。Python要求字符...

    蒙娜丽宁
  • Python编程思想(24):类的实例方法

    对于在类中定义的实例方法,Python会自动绑定方法的第1个参数(通常是self),第1个参数总是指向调用该方法的对象。由于实例方法(包括构造方法)的self参...

    蒙娜丽宁
  • python变量和基本数据类型

    #!/usr/bin/env python # -*- coding: utf-8 -*-

    py3study
  • python 购物车代码

    py3study
  • Python 强化训练:第八篇

    谢伟
  • Linux平台Firefox Flash插件安装方法

    正常情况下,在firefox目录中创建一个plugins目录,将libflashplayer.so文件拷贝进去即可。

    党志强
  • shell脚本应用的基本概念

    在shell脚本中,#表示注释,编写好的shell脚本可以通过“./脚本名”的方式执行脚本,但是需要文件本身具有x权限,还可以通过内部命令“source”或者“...

    小手冰凉
  • MySQL变量介绍和用法简介

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 ...

    用户1208223
  • 全网最易懂的正则表达式教程(1)- 入门篇

    https://www.cnblogs.com/poloyy/category/1796055.html

    小菠萝测试笔记

扫码关注云+社区

领取腾讯云代金券