Python - 面向对象编程 - 类变量、实例变量/类属性、实例属性

什么是对象和类

https://www.cnblogs.com/poloyy/p/15178423.html

 

什么是 Python 类、类对象、实例对象

https://www.cnblogs.com/poloyy/p/15178456.html

 

类变量、实例变量/类属性、实例属性

前言

只是叫法不一样

实例属性 = 实例变量

类属性 = 类变量

个人认为叫属性更恰当

 

类属性和实例属性区别

  • 类属性,所有实例对象共享该属性
  • 实例属性,属于某一个实例对象的属性,用于描述具体的对象

 

从实际栗子了解类属性、实例属性

有一个表格,四个常见的明星

姓名年龄
周润发 58
成龙 55
刘德华 53
周星驰 54

总结一下

  • 四个人归类为明星
  • 每个明星都有两个属性:姓名、年龄
  • 明星这个群体具有一个属性:明星数量,在这张表是 4
  • 姓名和年龄等属性是用来描述具体的一个对象
  • 明星的数量是用于描述明星这个类别

 

使用面向对象编程思想来总结的话

  • 周润发、成龙、刘德华、周星驰都是实例对象
  • 他们都属于明星,明星是
  • 属于实例对象的属性有:姓名、年龄,所以也叫实例属性
  • 属于明星类的属性有:数量,所以也叫类属性

 

类里面的三种类型变量

  • 在所有方法之外定义的变量,称为类属性/类变量
  • 在方法内部,通过 self.变量名 方式定义的变量,称为实例属性/实例变量
  • 在方法内部,通过 变量名=变量值 方式定义的变量,称为局部变量

 

类属性

类属性在类中的定义

class 类名:
    类属性1 = 值
    类属性2 =def func(self): 
        ...        

 

类属性、类方法注意点

  • 无论是类属性还是类方法,都无法像普通变量或者函数那样,在类的外部直接使用它们(类方法后面详解)
  • 可以将类看做一个独立的空间,类属性其实也是在类体中定义的变量,类方法是在类体中定义的函数
  • 需要通过类对象/实例对象来调用类属性 ClassName.classProperty (类方法后面详解)

 

类属性的栗子

# 类属性
class PoloBlog:
    # 这就是在所有方法之外 下面定义了 2 个类变量
    name = "小菠萝测试笔记"
    blog = "https://www.cnblogs.com/poloyy/"

# 通过类名调用类属性
print(PoloBlog.name)
print(PoloBlog.blog)


# 输出结果
小菠萝测试笔记
https://www.cnblogs.com/poloyy/

 

通过 Pycharm 的代码联想,可以看到 blog、name、__doc__ 三个类属性

  

类属性的调用方式

有两种

  • 直接通过类名调用
  • 也可以通过类的实例对象调用

 

调用类属性的栗子

# 调用类属性的两种方式
class PoloBlog:
    # 这就是在所有方法之外 下面定义了 2 个类变量
    name = "小菠萝测试笔记"
    blog = "https://www.cnblogs.com/poloyy/"


# 通过类名直接调用
print(PoloBlog.name)
print(PoloBlog.blog)

# 修改类属性
PoloBlog.name = "blogyuan"
PoloBlog.blog = "https://www.cnblogs.com/"

# 通过实例对象调用修改后的类属性
poloBlog = PoloBlog()
print(poloBlog.name)
print(poloBlog.blog)


# 输出结果
小菠萝测试笔记
https://www.cnblogs.com/poloyy/
blogyuan
https://www.cnblogs.com/

通过类名修改类属性的值,会影响所有的实例化对象

 

实例对象修改类属性

# 修改类属性
poloBlog.name = "小菠萝回来了"

# 再看看类对象调用修改后的类属性
print(PoloBlog.name)
print(poloBlog.name)


# 输出结果
blogyuan
小菠萝回来了
  • 会发现,类名.name 仍然返回之前的值,而 实例对象.name 会返回修改的值
  • 原因: 实例对象.name 本质上并不是修改类属性的值,而是在定义一个新的实例属性(下面详解)

 

动态添加类属性

PoloBlog.age = 24
print(PoloBlog.age)
print(poloBlog.age)


# 输出结果
24
24
  • age 没有在类体中定义
  • 可以直接通过 类名.new_property_name 的方式定义一个新的类属性

 

实例属性 

  • 属于具体对象的属性,用于描述具体的对象
  • 只能通过实例对象访问,无法通过类名访问

 

实例属性的栗子

class PoloBlog:
    def __init__(self):
        # 在方法内部,通过 self.name 的方式定义的变量就是实例变量
        self.name = "小菠萝测试笔记"
        self.add = "https://www.cnblogs.com/poloyy/"

    # 下面定义了一个 say 实例方法
    def say(self):
        self.age = 13

# 实例化对象
blog = PoloBlog()
blog.say()

print(blog.name, blog.add, blog.age)


# 输出结果
小菠萝测试笔记 https://www.cnblogs.com/poloyy/ 13
  • 重点:__init__ 会在实例化对象的时候自动调用,因此 blog 创建成功就有 name、add 两个实例属性
  • 调用 say() 方法之后才有第三个实例属性 age

 

修改实例属性的栗子

blog.name = "小菠萝"
blog.add = "xiaopolo.com"
blog.age = 24

print(blog.name, blog.add, blog.age)


# 输出结果
小菠萝 xiaopolo.com 24

 

动态添加实例属性 

blog.phone = 13501489999
print(blog.phone)


# 输出结果
13501489999

上面也有说到,通过 实例对象.属性名 的方式并不会给类变量赋值,而是定义一个新的实例变量

 

综合栗子

# 综合栗子
class PoloBlogObjectTest:
    # 类变量
    sum = 0

    # 初始化方法
    def __init__(self, name, age):
        # 实例变量
        self.name = name
        self.age = age
        # 类变量
        PoloBlogObjectTest.sum += 1

    # 实例方法
    def printNameAge(self):
        print(self.name, self.age)


poloTest1 = PoloBlogObjectTest("小菠萝一号", 24)
poloTest2 = PoloBlogObjectTest("小菠萝二号", 14)

print(PoloBlogObjectTest.sum)
# 调用实例方法
poloTest1.printNameAge()
poloTest2.printNameAge()


# 输出结果
2
小菠萝一号 24
小菠萝二号 14

 

不推荐实例属性和类属性同名

  • 类中,实例属性和类属性可以同名
  • 但这种情况下使用实例对象将无法调用类变量,它会首选实例变量,无论这个变量是否已定义
  • 实例独享绑定新的实例属性时,会直接覆盖掉重名的类属性

 

实例属性、类属性同名栗子

class Person:
    # 只有一个类变量
    name = "cool guy"

# 实例化一个对象
p = Person()
# 打印实例属性 name,因为实例对象并没有name属性,所以会继续查找class的name属性
print(p.name)
# 打印类属性 name
print(Person.name)

# 给实例绑定 name、age 属性
p.name = "bad guy"
p.age = 12

# 打印 name、age 属性
print(p.age)
# 由于实例属性优先级比类属性高,因此,它会屏蔽掉类的 name 属性
print(p.name)
# 仍然打印类的 name 属性
print(Person.name)


# 输出结果
cool guy
cool guy
12
bad guy
cool guy

 

实例对象属性引用的查找过程

 

posted @ 2021-08-28 23:46  小菠萝测试笔记  阅读(2253)  评论(2编辑  收藏  举报