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

44 篇文章 4 订阅
33 篇文章 47 订阅

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

《Python编程思想》总目录

《Python编程思想》专栏

 

目录

1. 类变量和实例变量

2. 使用 property函数定义属性


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

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.py

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方法,因此该属性是一个读写属性,不能删除。

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

子龙,赵
云长
关

-----------------支持作者请转发本文,也可以加李宁老师微信:unitymarvel,或扫描下面二维码加微信--------

欢迎关注  极客起源  微信公众号,更多精彩视频和文章等着你哦!

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值