类与对象

一、类、类对象、实例对象三者关系

class Shuai:
	content = '帅健最帅'

	def con_print(self):
            print(content)

# 以上代码进行的是“类的定义”,即称为类。

a = Shuai # 类名不带括号,即为类对象

b = Shuai() # 类名带括号,即为实例对象

1.1 类

这里的类指的是,定义类内容的过程。

1.2类对象与实例对象的区别

  • 从形式上:类对象不带括号,而实例对象带括号。(联想:对于函数,不带括号时系统认为是函数变量,带括号系统则认为进行了具体的函数操作)
  • 从功能上:类对象与实例对象的关系就好比,苹果与‘红苹果、青苹果’之间的关系。苹果是对‘红苹果、青苹果’的统称;而‘红苹果、青苹果’是苹果的具体品种,具有苹果的一般形状(属性)与功效(方法)

二、类的基本定义(属性、方法)

   类是由属性方法两大部分构成。其中属性 == 变量赋值,方法 == 函数定义。

2.1 属性

   类的属性可以分为类属性与实例属性。

class Shuai:
    content = 'best'           # 这是 类属性

    def __init__(self):   
        self.x = 4             # 这是实例属性
        self.y = 5             # 这是实例属性
        Shuai.count = 6        # 这是 类属性(类名.属性名)

2.1.1 实例属性

   实例属性是指只有实例对象才拥有的属性,而类对象没有。若对类对象访问实例属性则会报错。

>>> Shuai.x
Traceback (most recent call last):
  File "<pyshell#7>", line 1, in <module>
    Shuai.x
AttributeError: type object 'Shuai' has no attribute 'x'

实例属性的定义主要是通过方法内的self.变量名赋值来实现以及类定义后实例对象.变量赋值两种途径。

  • 注意1:第二种定义方法中,定义得到的实例属性只适用于该实例对象;其他实例对象没有。(通俗的说就是:可以给特定的实例对象赋予新的属性)
  • 注意2:每个实例对象的实例属性可以通过后期赋值改变而各不相同,没有约束。
  • 注意3:self 的意义就是对实例对象的指代,以供后期实例对象调用属性和方法时,能找到对应的内容。

2.1.2 类属性

   类属性简单来说就是,类对象拥有的属性,即对类对象访问类属性不会报错。

>>> Shuai.content
'best'
>>> Shuai.count
6

类属性主要是通过方法外的变量赋值方法内的类名.变量赋值两个个途径来定义。
实例对象与类对象关于类属性的联系与区别见以下代码解释。

>>> a = Shuai()
>>> b = Shuai()
>>> print(Shuai.content,‘\n’,a.content,‘\n’, b.content)
best
best
best
# 说明:实例对象也可以对类属性进行访问。

>>> Shuai.content = 'best and best'
>>> print(Shuai.content,'\n',a.content,'\n',b.content)  
best and best 
best and best 
best and best
# 说明:对类对象进行类属性的重新赋值,实例属性所访问的类属性也会随之改变。

>>> b.content = 'last' # 新建一个与‘类属性同名’的实例属性
>>> print(Shuai.content,'\n',a.content,'\n',b.content)
best and best 
best and best 
last
# 说明:新建的‘类属性同名’的实例属性会覆盖掉实例对象与该类属性的联系

>>> Shuai.content = 'best and best and best'
>>> print(Shuai.content,'\n',a.content,'\n',b.content)  
best and best and best 
best and best and best
last
# 说明:该实例对象的该属性不会随着类属性的变化而变化

以下是类属性的关系流程图:
类属性访问流程图
在这里插入图片描述
在这里插入图片描述

2.1.3 类属性与实例属性的具体作用

  • 类属性一般仅用来跟踪与类相关的值,比如统计实例对象的数量等等。但要注意不要使新建的实例属性覆盖了与类属性的联系
  • 而实例属性是一个更为常用的属性,能够使得每个实例对象的初始值相同,而对某一实例对象的修改,不影响其他实例对象。

2.1.4 属性的访问限制

由于类对象就是一个封装的代码,便可能有些不想让外部访问的属性。在Python中,可以用双下划线“__”开头命名属性。这类属性可以在类定义过程中被调用,而在外部不能被调用。

class Student(object):

    def __init__(self, name, score):
        self.__name = name
        self.__score = score

    def print_score(self):
        print('%s: %s' % (self.__name, self.__score))

以上代码运行后,尝试访问__name属性:

>>> a1 = Student('帅健',150)
>>> a1.__name
Traceback (most recent call last):
  File "<pyshell#1>", line 1, in <module>
    a1.__name
AttributeError: 'Student' object has no attribute '__name'

实际上,Python中实现该访问限制功能的方法是,将双下划线“__”开头的属性自动改为了“_ 类名 __属性名”。可在类定义后采用该方法访问。

>>> a1._Student__name
'帅健'

但是强烈建议你不要这么干,因为不同版本的Python解释器可能会把双下划线“__”开头的属性名改成不同的属性名。
此节内容可参考廖雪峰的教程。廖雪峰教程关于访问限制的描述

2.2 方法

方法可分为实例方法,类方法和静态方法。一般我们用到的都是实例方法。

2.2.1 实例方法

定义方式

def 方法名(self,*其他变量):
  • 第一个形参必须是self,用于指代实例对象。在方法中可以通过“self.属性名”来访问与修改实例对象的属性。

调用方式

实例对象.方法名()
  • 实例方法只有实例对象能够调用,而类对象不能调用。
  • 实例对象在调用实例方法时,self会自动与该实例对象进行绑定。无需再输入self的实参,直接从其他变量开始输入。

举例(打印学生成绩)

class Student(object):
    
    def __init__(self, name, score): 
        self.__name = name
        self.__score = score
        
    def print_score(self):
        print('%s: %s' % (self.__name, self.__score))

运行并调用实例方法,结果如下:

>>> a = Student('张三',60)
>>> a.print_score()
张三: 60

魔法方法见后期单独讲。

2.2.2 类方法

作用:

  • 类方法的作用,一般就是不基于实体对象,用类对象去获取一些与类有关的量。

定义方式:

@classmethod #修饰器
def 方法名(cls,*其他变量):
  • 类对象只有类对象能够调用,而实例对象不能调用。
  • 类对象在调用实例方法时,cls会自动与该实例对象进行绑定。无需再输入cls的实参,直接从其他变量开始输入。

举例(利用类方法,获取实例对象的数量)

class Student(object):
    __count = 0
    
    def __init__(self, name, score):
        self.__name = name
        self.__score = score
        Student.__count += 1  # 在实例方法中要调用类属性,则应该用“类名.类属性名”来调用
        
    def print_score(self):
        print('%s: %s' % (self.__name, self.__score))

    def __del__(self):
        Student.__count -= 1

    @classmethod  #装饰器,即将定义类方法
    def getCount(cls):
        return cls.__count

运行并调用类方法,结果如下:

>>> a = Student('张三',60)
>>> b = Student('李四',70)
>>> c = Student('王五',80)
>>> Student.getCount()
3

2.2.3 静态方法

作用:

  • 静态方法一般用于无需调用类属性或者实例属性的方法中。这样子的方法既可以用于类对象,又可以用于实例对象中。

定义方法:

@staticmethod #修饰器
def 方法名(*其他变量):
  • 静态方法的变量无需self或者cls,直接输入其他形参即可。
  • 类对象与实例对象都可以使用静态方法。

调用:

类名.方法名()
实例对象名.方法名()
  • 不同实例对象的静态方法的内存地址相同,即完全一样。

举例:(打印老师名字)

class Student(object):

    @staticmethod  #静态方法修饰器
    def print_teach():
        print('该班级的班主任是帅健')

运行并调用静态方法,结果如下:

>>> a = Student()
>>> a.print_teach()
该班级的班主任是帅健
>>> Student.print_teach()
该班级的班主任是帅健
>>> a.print_teach is Student.print_teach
True

2.4 一些注意事项

  • 若属性与方法名字相同,则新的会覆盖旧的。经常出现‘对实例对象赋予新的属性名,而覆盖掉与之同名的方法’的情况。

三、类的继承

3.1 简单的继承

3.1.1 继承的作用:

  • 子类可以得到父类所有的属性与方法,无需重新写代码。
  • 继承后,可以对父类的属性与方法进行覆盖,从而修改或删除不需要的属性或方法。

3.1.2 继承的形式:

class 子类名(父类名):

举例

class Animal(object):
    def run(self):
        print('Animal is running...')

class Dog(Animal):
    pass

运行并调用,结果如下:

>>> Animal().run()
Animal is running...
>>> Dog().run()
Animal is running...
  • 注意:一个类可以继承多个类,在括号中用逗号隔开多个父类即可。

3.1.3 对父类的覆盖

  • 父类的属性与方法是无法删除与修改的。
  • 只能通过在子类中重新定义同名属性与方法,以覆盖掉父类的相关属性与方法。
  • 其中定义同名方法内容用“pass”占位,可实现父类方法无效化。

举例:

class Cat(Animal):
    def run(self):   #通过定义新的方法,从而使父类的同名方法在该子类中无效
        pass         #可以填写新的方法内容,也可以使用“pass”占位。

运行并调用,结果如下:

>>> Animal().run()
Animal is running...
>>> Cat().eat()
Animal is eating...  # Cat类的run方法被重新定义了

3.1.4 在子类中调用同名父类方法

当子类中定义有与父类同名的方法,但又想在方法中调用重名的父类方法,有以下两种方法可以实现。

(1)父类名.方法名(self)

举例

class Animal(object):
    def run(self,speed):
        print('The animal is running with %s km/h' % speed)

class Dog(Animal):
    def run(self,speed):
        print('The animal is a dog')
        Animal.run(self,speed)

运行并调用,结果如下:

>>> Dog().run(10)
The animal is a dog
The animal is running with 10 km/h
  • 注意:- 注意1:在使用该方法调用父类方法时,必须添加self形参,再输入其他形参。即父类方法的形参全部输入。
  • 注意2:对于父类方法需要的形参,要在进行调用的方法中设置。(见第6行代码)
(2)super().方法名(*其他变量)

举例

class Animal(object):
    def run(self,speed):
        print('The animal is running with %s km/h' % speed)
        
class Dog(Animal):
    def run(self,speed):                 # 输入父类方法需要的形参,方便传入父类方法中
        print('The animal is a dog')
        super().run(speed)               # super函数

运行结果如下:

>>> Dog().run(10)
The animal is a dog
The animal is running with 10 km/h
  • 注意1:在使用super函数调用父类方法时,无需添加self形参,输入其他形参即可。
  • 注意2:对于父类方法需要的形参,要在进行调用的方法中设置。(见第6行代码)
(3)两种方法的比较
  • 由于第一种方法需要指定父类名字,而super方法会自动寻找父类。故现在用super函数比较方便,也便于后期修改代码。

3.2 多重继承

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值