python面向对象中类对象、实例对象、类变量、实例变量、类方法、实例方法、静态方法

时间:2021-11-05 17:24:01

 1. 类对象和实例对象

  Python中一切皆对象,Python类本身也是一种对象,类定义完成后,会在当前作用域中定义一个以类名为名字的命名空间。类对象具有以下两种操作:

  • 可以通过“类名()”的方式实例化一个对象。
  •  可以通过“类名.类属性”的方式来访问一个类属性。

  如果说类时一种概念性的定义,是一种类别,那么实例对象就是对这一类别的具体化、实例化,即实例化对象是类对象实例化之后的产物。

class Person:# 声明一个类对象

    pass

p1 = Person()#声明一个实例对象

print(Person)#输出结果:<class '__main__.Person'>

print(p1)#<__main__.Person object at 0x0000015F7F94D0F0>

2 类变量与实例变量

2.1 概念上的区别

  类变量是指是指该类的所有实例说共有的数据,实例变量是该类每一个实例所特有的数据。这么说的话可能还是很抽象,我们拿人类(Person类)来打比方,人类能移动(move=True),这是每一个人(张三、李四)都能做的,所以我们可以说人类能移动,张三能移动,李四也能移动,这里的移动(move)就是一个类变量。但每一个人都可能有不同的姓名(name)和年龄(age),张三可能20岁,李四可能30岁,但是我们不能说人类(Person)都是20岁或30岁,这里的姓名(name)和年龄(age)就是实例变量。

class Person:

    move = True    # 这是类变量

    def __init__(self , name , age):

        self.name = name  # 这是实例变量

        self.age = age  # 这是实例变量

2.2 声明上的区别

  类变量声明通常在类内部,但函数体外,不需要用任何关键字修饰。实例变量一般声明在实例方法内部(其他方法内部也不行),且用self关键字修饰。

class Person:

    move = True    # 这是类变量,


    def __init__(self , name):

        self.name = name  # 这是实例变量,必须声明在实例函数内,用self关键字修饰

        # move = True  # 类变量不能再函数体内声明,在这个位置声明的又没有self关键字修饰,只能是一个局部变量


    # self.age = age  # 这是错误的,实例变量不能再这里声明

   eat = True  # 这是类变量,可以在函数体外,类内部任意位置

  上面的变量绑定都是在对象声明过程中绑定的,但事实上类变量和实例变量都可以在类或者实例都可以在对象声明结束之后再绑定。“类名.变量名”绑定的是类变量,“实例名.变量名”绑定的是实例变量。

class Person:

    move = True   

    def __init__(self , name , age):

        self.name = name 

        self.age = age 

 

p1 = Person('张三' , 20)

p1.gender='' # 声明实例对象结束之后再绑定一个实例变量

Person.eat = True  # 声明类对象结束之后再绑定一个类变量

print(p1.gender)  # 输出结果:男

print(p1.eat)  #输出结果:True

  注:虽然可以在对象声明之后再绑定对象,但是这种方式最好不要使用。

2.3 访问上的区别

  类变量可以通过“类名.变量名”和“实例名.变量名”的方式访问。实例变量只能通过“实例名.变量名”的方式来访问。

class Person:

    move = True    # 这是类变量

    def __init__(self , name , age):

        self.name = name  # 这是实例变量

        self.age = age  # 这是实例变量


p1 = Person('张三' , 20)

print(p1.name  , p1.age) # 通过“实例名.变量名”的方式访问实例变量

print(p1.move) # 通过“实例名.变量名”的方式访问实例变量

print(Person.move)  # 通过“类名.变量名”方式访问类变量

# print(Person.name) # 这是错误的

  注:虽然可以通过“实例名.类变量名”的方式访问类变量,但是并不推荐,最好还是通过“类名.类变量名”来访问类变量。

2.4 存储上的区别

  类变量只会在用class关键字声明一个类时创建,且也只会保存在类的命名空间中,这个类的实例的命名空间中是没有的。通过“实例名.类变量名”访问类变量时,实际访问的是类命名空间中数据,所以所有实例访问到的数据都是同一个变量。实例变量保存在实例各自的命名空间中。

class Person:

    move = True    # 这是类变量

    def __init__(self , name , age):

        self.name = name  # 这是实例变量

        self.age = age  # 这是实例变量

p1 = Person('张三' , 20)

p2 = Person('李四' , 30)

# 通过id()函数查询move内存地址

print(id(p1.move))   # 输出结果为:1622667424

print(id(p1.move))   # 输出结果为:1622667424

3 静态方法、类方法、实例方法

3.1 静态方法

  静态方法是指在定义时,使用@staticmethod装饰器来修饰,无序传入self或cls关键字即可进行创建的方法。在调用过程时,无需将类实例化,直接通过“类名.方法名()”方式调用方法。当然,也可以在实例化后通过“实例名.方法名()”的方式调用。在静态方法内部,只能通过“类名.类变量名”的方式访问类变量。

class Person:

    move = True

    def __init__(self , name , age):

        self.name = name

        self.age = age

    @staticmethod

    def static_fun(): # 声明一个静态方法

        print(Person.move)

p1 = Person('张三' , 20)

p1.static_fun() #输出结果:这是静态方法

Person.static_fun() #输出结果:这是静态方法

3.2 类方法

  类方法需要使用@classmethod装饰器来修饰,且传入的第一个参数为cls,指代的是类本身。类方法在调用方式上与静态方法相似,即可以通过“类名.方法名()”和“实例名.方法名()”两种方式调用。但类方法与静态方法不同的是,类方法可以在方法内部通过cls关键字访问类变量。在类方法内部,既能通过“类名.类变量名”的方式访问类变量,也能通过“cls.类变量名”的方式访问类变量。

class Person:

    move = True

    def __init__(self , name , age):

        self.name = name

        self.age = age

    @classmethod

    def class_fun(cls): # 声明一个类方法

        print(cls.move)

        print(Person.move)# cls 指的就是Person类,等效

p1 = Person('张三' , 20)

p1.class_fun() #输出结果:True True

Person.class_fun() #输出结果:True True

3.3 实例方法

  在一个类中,除了静态方法和类方法之外,就是实例方法了,实例方法不需要装饰器修饰,不过在声明时传入的第一个参数必须为self,self指代的就是实例本身。实例方法能访问实例变量,静态方法和类方法则不能。在实例方法内部只能通过“类名.类变量名”的方式访问类变量。在调用时,实例方法可以通过“实例名.实例方法名”来调用,如果要通过类来调用,必须必须显式地将实例当做参数传入。

class Person:

    move = True

    def __init__(self , name , age):

        self.name = name

        self.age = age

    def instance_fun(self): # 声明一个实例方法

        print(Person.move) # 访问类变量

        print(self.name , self.age)

p1 = Person('张三' , 20)

p1.instance_fun()

Person.instance_fun(p1) #通过类访问实例方法时,必须显式地将实例当做参数传入