Python_类属性

1. 创建对象的过程以及实例的概念

实例是面向对象开发使用的术语, 在我们使用面向对象开发时,第一件要做的事情是设计 类, 根据对象的职责把对象的属性和方法封装到一个抽象的类中, 我们在初始化方法中来定义对象的属性, 对象的方法只需要使用def 关键字定义在类的内部就可以, 同时对象的方法第一个参数应该是self, 因为我们在对象的方法内部使用self 可以访问到当前这个对象的属性,或者调用当前这个对象其他的对象方法。当类定义完成之后, 我们使用类名来创建对象时, python 的解释器会帮我们做两件事情, 第一件事情,现在内存中为对象分配空间, 假设我们要创建一个对象,python 的解释器先会为这个对象在内存中分配一个空间, 第二件事情, 会自动调用初始化方法, 在初始化方法内部来给对象定义对应的属性, 这个就是我们使用类名创建对象的两件事情。

假设已经创建一个对象1, 如果我们使用这个类名在创建一个对象2, 对象1和对象2 的属性保存在的内存空间的地址是不同的,因为对象与对象之间属性与属性之间可以各不相同, 譬如,有个人叫小明,有个人叫小花, 小明和小花的名字各不相同, 因此对象的属性应该各自保存在各自不同的内存空间。

假设我们在每个对象中, 都为对象的方法单独的保存一下, 创建有多少个对象,我们就分配多少的内存空间,这种方式并不好,而且关键的是对象的方法我们一旦编写完成,在运行的时候, 方法并不会改变了,因此而Python解释器在处理的时候,对象的方法在内存中只有一份,而我们在调用这个方法的时候,会把调用方法的对象引用传递给第1个参数self,这样呢,在方法内部就可以使用self调用这个方法的对象的属性,或者调用其他对象的方法了, 这个就是在Python中创建对象的完整过程.

当一个对象创建完成之后,在内存中这个对象就有了一个实实在在的存在,所以啊,在开发时,经常还会把创建出来的对象叫做实例,一个实实在在存在的例子就叫做实例,所以呢,在开发时经常会把创建出来的对象叫做由这个类创建出来的实例,同时把创建对象的动作叫做实例化,使用这个类实例化一个对象出来.

而对象的属性呢,通常会叫做实例属性,对象的方法呢,会叫做实例方法,这样就是实例这个术语,一句话讲, 实例就是由类创建出来的实实在在的存在, 由类创建出来的对象,就叫做实例.

 

当创建了多个对象之后,多个对象的属性在内存中是这么保存的, 当创建了多个对象,每个对象都各自有自己独立的内存空间,在这些内存空间中, 保存每个对象各自不同的属性.

当我们使用一个类创建多个对象之后,这多个对象的方法在内存中保存只有一份,而在调用这些方法时,只需要把调用的对象的引用传递给这个方法的第1个参数就可以.

2. 类是一个特殊的对象

在Python这门语言中啊,类是一个特殊的对象. 在使用class关键字定义了一个类,那么程序在运行的时候,这个类会被加载到内存. 下面这张示意图,定义了一个类之后,在程序运行时, 可以使用这个类来创建出很多个对象. 在对象的内存空间中保存的是不同对象各自不同的属性. 对象的实例方法是保存在类所在的内存空间的.

当让某一个对象调用实例方法时,这个对象会把自己的引用当作参数传递给这个方法,因此对象的实例方法是保存在类所在的内存空间的,同学们类在运行的时候, 同样也会被加载到内存,在Python这门语言中一切皆对象,而我们使用class关键字,定义了一个类之后,我们呢可以把这个类叫做类对象, 类在Python中是一个特殊的对象. 当有了类对象之后,再使用这个类创建出来的对象,就把它叫做实例对象. 一个类对象, 一个实例对象,

程序在运行的时候,类对象在内存中只需要有一份就可以,因为类只是一个模板,使用这个模板可以创建出很多个实例,实例可以有很多个, 模板只需要有一个就可以. 在Python中, 类是一个特殊的对象,程序运行时, 类同样也会被加载到内存,并且类对象在内存中只有一个. 既然类对象是一个特殊的对象,而按照面向对象概念,我们可以给对象定义不同的属性以及方法,作为特殊的对象,类对象,也可以给它指定类对象自己特有的属性和方法,给类对象定义的属性就叫做类属性,给类对象定义的方法就叫做类方法. 一个类属性, 一个类方法,之所以这样命名,就是为了跟实例的属性和方法加以区分,在Python中类是一个特殊的对象,既然是特殊的对象,同样可以给这个对象来定义类的属性以及类的方法,在程序开发中啊,可以通过类名点的方式,访问到类的属性以及类的方法.

3. 类属性的定义和使用

定义类属性,所谓类属性,就是给一个类对象定义的属性,在开发中,如果希望给一个类定义属性,那么就可以在类的下方, 使用一个赋值语句就可以, 赋值语句左侧的变量名, 就是类属性的名称,而在开发中通常会使用类属性,来记录一些跟这个类相关的特征. 跟类相关的特征,就是

比如要开发一个工具,每一个工具都有各自不同的名称,而在开发中需要知道使用这个工具类到底创建了多少个工具对象,遇到这种开发需求,就可以在工具类中定义一个叫count的类属性, 由count这个类属性来记录一下当前创建的工具对象总数,类属性定义完成,这个属性值应该这样变化, 在初始化方法内部,当每要创建一个对象的时候,就把类属性的数值做一个加1操作,这样在需要的时候,只需要把这个类属性打印一下,就可以知道当前使用工具类到底创建了多少个工具对象.

首先使用class关键字来定一个工具类,让这个工具类继承自object,

 

类名准备完成,可以使用赋值语句来定义类属性,来记录所有工具对象的数量.给这个类属性起个名字叫做count,并且设置一个初始值0,现在类属性定义完成,

 

类属性的变化,可以在初始化方法内部, 当每次要创建工具对象的时候,就让这个类属性的值做一个+1. 先给初始化方法增加一个name的形参,先给工具类定义一个name的属性, 把形参传递给这个属性,

 

然后每次调用初始化方法的时候,应该让类属性的值做一个+1,但访问类属性, 注意不能使用self,而应该使用工具的类名,在类名后面跟上一个点, 在点后面找到count属性,写个+=1,每一次调用初始化方法,都让工具的技术做一个加1,一个简单的工具定义完成.

 

现在就可以在主程序中创建工具对象,先来创建第1个对象,tool1, 使用工具的类名,并且指定一下工具的名称,斧头,我们先创建一个斧头的工具,

 

再使用print函数输出工具对象的总数, 来我们使用print函数做个输出,要想访问类属性,同样需要通过类名点的方式,在点后面把类属性的名称跟上就可以.

 

运行一下程序走,控制台输出了一个1,因为现在使用工具类只创建了一个工具

现在再来创建一个工具, 老师拿起个tool2, 并且使用工具类再创建一个工具,给工具起个名字叫做"榔头",

 

创建完成再运行一下程序,控制台输出了一个2.

 

再创建一个工具,给工具起个名字"水桶",第3个工具创建完成,

 

再来运行一下,现在工具的总数就变成了3,

 

检查一下代码,在工具类的下方使用赋值语句定义了一个类属性,这个类属性记录了工具这个类所特有的特征,让这个count中记录了使用工具类到底创建了多少个工具对象,而每一次初始化方法被调用的时候,我们都会在初始化方法的内部,针对这个类属性做一个加1,这样呢, 就可以在需要的时候, 直接把创建对象的总数做一个输出了,这个就是类属性的使用.

一句话讲要定义类属性,就在类名下方使用赋值语句就可以赋值,语句左侧的变量名就是类属性的名称,而在开发时,通常是使用类属性来记录跟类相关的特征,

 

要强调一下对面类属性是记录跟类相关的特征的, 类属性是不会用来记录跟具体对象相关的特征的. 回顾一下,类是一个模板, 程序运行时内存中类只有一个, 而使用这个模板可以创建出非常非常多的对象,因此类属性专门是用来记录跟这个模板相关的特征的,而不会使用类属性来记录跟对象有关的特征.

完整代码:

# 类属性的定义和使用
class Tool(object):

    #使用赋值语句定义类属性, 记录所有工具对象的数量
    count = 0

    def __init__(self, name):
        self.name = name 

        # 让类属性的值+1
        Tool.count += 1

# 1. 创建工具对象
tool1 = Tool("斧头")
tool2 = Tool("榔头")
tool3 = Tool("水桶")

# 2. 输出工具对象的总数
print(Tool.count)

运行结果:

 

4. 属性查找机制

 

所谓属性获取机制,就是在编写代码的时候,在一个变量后面跟上点, 再在点后面跟上要访问的属性, Python解释器是怎样找到这个属性的值的,这个就叫做属性获取机制.

同时在Python中, 获取属性值是存在一个向上查找机制的. 看一下这个类图,大概在工具类中定义了两个属性,一个类属性count, 一个实例属性name,要访问类属性, 是通过类名点的方式来访问的.

但是在Python中,除了用类名访问类属性之外,还可以使用变量名来访问类属性。Python解释器是怎么做到的呢?再看一下这张类图,假设使用工具类创建了一个工具叫做水桶,变量名呢,叫tool1, 在开发时, 可以使用tool1这个变量名,同样也可以访问到count这个类属性,那程序执行时Python解释器是怎么做的呢?

Python解释器,会首先在对象内部来查找是否有count的属性,

 

 

如果有就直接返回,如果在对象内部没有找到这个属性,那么Python解释器就会向上寻找类中是否存在类属性,

 

 

如果找到就输出,如果没有找到呢,就报错,这样就是Python中的属性获取, 向上查找的机制.

在上一小节输出工具对象总数时,是使用工具这个类做的输出.

 

那如果把这一行代码注释一下,在下方使用print函数,先写一下"工具对象总数",然后跟上个%d,在引号后面我们使用tool1 这个变量来输出一下count属性,

 

运行程序看结果, 工具对象总数是3, 跟之前使用类名得到的结果是完全一样的.

如果把tool1改成tool3, 运行验证一下, 控制台输出的结果仍然是3,跟之前使用得到的结果是完全一样的.

 

那现在这个程序在执行时是怎样处理工具类中的类属性呢?Python解释器在执行时是从上向下顺序执行的, 当来到第1行发现定义的是一个类, 并不会立即执行,而是会向下方寻找可以执行的代码,看14行代码, 在内存中创建一个斧头的工具,在工具对象中只会记录工具的名称name.

同时在执行初始化方法的时候,注意初始化方法中要修改类属性,因此就会把类中定义的count这个类属性, 把数值从0改为1, 初始化方法执行完成,斧头这个工具在内存中已经存在了.

 

那么代码继续向下执行,在第15行又创建了一个"榔头"的工具,在执行初始化方法时,又会把类中的count属性再做一个+1,现在工具属性变成了2,那么代码继续向下执行,继续在内存中创建一个"水桶"的工具,同时呢在执行初始化方法的时候,会把类属性这个2再做一个加1, 从2变成了3.

当执行第20行代码的时候,打印tool3的count属性.

 

那要打印这个属性的时候, Python解释器是这样来处理的, 首先啊Python解释器会在tool3 这个对象中来查找有没有count属性,如果有就直接输出,那如果没有呢,就会按照向上寻找的方式来向上查找创建工具对象的类,

   

在这个类中来查找有没有count的类属性,如果找到就在控制台, 把count类属性的值做个输出,这个就是现在完成的代码具体执行的情况.

Python解释器在获取属性时是存在一个向上查找机制的,所以要获取类属性,既可以通过类名也可以通过对象的方式, 都可以获取到类属性. 在编写代码时, 是应该使用类名还是使用对象名, 获取类属性,唉,应该使用类名获取类属性,因为使用对象名虽然可以获取到,但是使用这种方式编写的代码非常容易产生混淆,容易产生阅读代码的困惑,所以要访问类属性就应该使用类名来访问,而不要使用对象名来访问.

完整代码:

class Tool(object):

    # 使用赋值语句定义类属性,记录所有工具对象的数量
    count = 0

    def __init__(self, name):
        self.name = name 

        #让类属性的值+1
        Tool.count += 1

# 1.创建工具对象
tool1 = Tool("斧头")
tool2 = Tool("榔头")
tool3 = Tool("水桶")

# 2. 输入工具对象的总数
# print(Tool.count)
print("工具对象总数 %d" % tool1.count)

执行结果:


 

5. 使用对象名+类属性赋值语句会创建实例属性

 

使用对象名点的方式,在访问类属性时存在陷阱,使用类名点可以访问类属性,使用对象名点也可以访问类属性, 但是对象名点 访问类属性这种方式并不推荐,因为在开发中如果使用对象名点跟上类属性的名称, 再使用一个赋值语句,不小心就会掉到陷阱里了.

使用水桶这个工具来输出了类属性count的值,现在运行程序, 输出结果应该是3,因为我们一共创建了三个对象.先运行一下,输出的对象总数是3,

class Tool(object):

    # 使用赋值语句定义类属性,记录所有工具对象的数量
    count = 0

    def __init__(self, name):
        self.name = name 

        #让类属性的值+1
        Tool.count += 1

# 1.创建工具对象
tool1 = Tool("斧头")
tool2 = Tool("榔头")
tool3 = Tool("水桶")

# 2. 输入工具对象的总数
# print(Tool.count)
print("工具对象总数 %d" % tool1.count)

运行结果:

 

 

现在把第19行的注释删掉,在第十九行使用"水桶"这个变量来调用count属性,现在看水桶这个变量.count属性,tool3.count, 然后呢,使用赋值语句给这个属性设置一个值,tool3.count = 99, 第20行代码在输出的时候会输出99,

 

因为写了一个赋值语句,现在老师是一个f时走唉,同学们看控制台果然输出了99.

class Tool(object):

    # 使用赋值语句定义类属性,记录所有工具对象的数量
    count = 0

    def __init__(self, name):
        self.name = name 

        #让类属性的值+1
        Tool.count += 1

# 1.创建工具对象
tool1 = Tool("斧头")
tool2 = Tool("榔头")
tool3 = Tool("水桶")

# 2. 输入工具对象的总数
tool3.count = 99
print("工具对象总数 %d" % tool3.count)

运行结果:

 

但是现在在工具类中,count这个类属性保存的值到底是多少呢?把光标放在第21行,使用print函数做个输出,使用工具这个类名来输出一下对象的计数数量,再运行一下程序,

 运行结果:

 

类属性的值是3,而使用赋值语句修改之后,水桶的count输出是是99,出现这种情况的原因是, 先画一个示意图,Python的程序在执行时, 是从上向下顺序执行,当发现是一个类定义的时候,并不会立即执行,会向下方来寻找可以执行的代码,但是类已经被加载到内存,

在执行第14行语句的时候,就会在内存中为斧头分配一个空间,然后在执行初始化方法的时候,会给类属性做一个+1,0+1, 现在类属性应该是数字1,代码继续向下, 再来创建一个"榔头"的对象,同样会为"榔头"也分配一个内存空间,同时在执行初始化方法的时候会把类属性的值, 再做一个加1,1+1=2, 代码继续向下执行,为水桶同样也分配一个内存空间,然后再执行初始化方法的时候,把工具的计数修改为3.

第19行代码是使用赋值语句设置属性值,那么Python解释器在执行的时候,并不会按照向上搜索去找类属性,而Python解释器在执行赋值语句的时候, 就会直接在''水桶"变量中来查找一下, 有没有count属性,如果没有就在对象中直接添加一个count属性,并且把初始值设置成99,

 

因此第20行代码在执行的时候,就在控制台, 把水桶这个对象中count属性的值99, 做个输出,但是第十九行代码并没有修改到类属性, 所以啊, 在第21行代码执行的时候,通过类名来访问类属性,在控制台输出的仍然是数字3,这个就是使用对象名点 访问类属性的一个陷阱,一句话讲如果只是获取属性值不会有任何的问题,

但是如果使用赋值语句给这个属性设置值的时候,如果前面使用的是变量名,那么就会在对象内部添加一个属性,而不会修改到类属性的值,

 

这个就是使用对象名点的方式跟上类属性在开发时存在的一个陷阱. 强调在开发时要访问类属性应该用类名点的方式, 不要用对象点的方式来访问类属性,对象点访问类属性读取值没有任何的问题,但是设置值不会修改到类属性的值,而只是会在对象内部添加一个实例属性.
 

 

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值