深刻理解Python的类

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_29027865/article/details/89844083


在这里插入图片描述

类的定义

类的最基本的作用就是封装,类只负责去定义,去刻画一些东西。
类的首字母要大写,变量的名字要小写;
变量的两个单词间使用下划线来进行连接,如:student_number,但是类不用下划线来连接:StudentHomework;

例子:

class Student():
    name = ''
    age = 0
    # 类中的方法的参数必须加self
    def print_file(self):
        print('name:' + self.name)
        print('age:' + str(self.age))

# 要想使用这个类,就需要先对类进行实例化
student = Student()
# 调用类下面的方法
student.print_file()

注:不要在一个模块下面又去定义类,又去完成类的实例化和类下面方法的调用。模块如果写类的话,那这个模块就只写类,而把对类的实例化和使用放在另外一个模块里去,再在另外一个模块中通过from … import … 引入来对类进行实例化

函数与方法的区别

方法是一个设计层面上的称谓,函数是程序运行、过程式的称谓;
对于定义在类中的,称作方法比较合适,因为类是面向对象最基本的概念、是面向设计的。而定义在模块里的方法,称作函数比较合适;
变量在模块中,定义为变量;而变量定义在类中,一般称为数据成员,数据成员是为了体现出类的封装性,每一个变量都被类视为一个数据,这样的数据用来描述类的特性;

类与对象

类和对象到底是什么,它们之间又有一个什么样的关系?
类和对象是通过实例化关联到一起的,类是现实世界或思维世界中的实体在计算机中的反映,它将数据(数据成员)以及这些数据上的操作(方法)封装到一起,即类是用来刻画某些事物基本特性的。
做面向对象的原因也就是为了在计算机的世界中,把现实世界中的一些事物映射到计算机中。
我们用数据成员来刻画事物的一些特征,用方法来刻画事物的一些行为。比如学生类中,学生的name和age是特征,写作业,上课就是行为。
类设计的好坏与否就是行为和特征你怎样定义,行为要对应设计的主体。
类是一个抽象的概念,是一类事物的总称,并不是具体。而表示具体的东西,就是对象,类只是告诉我们这一类东西中有什么特征,但是特征具体取什么值,需要通过实例化来完成。当类被实例化后,它就变为了一个具体的对象。类可以理解为一个模板,通过这个模板,可以创造出不同的对象。

构造函数

实例化就是用类的模板来创建一个新的对象,那么怎么让模板实例化后的对象不相同呢?类比一下函数,我们会对函数定义不同的形参,然后传递不同的实参到函数的内部,由此让函数返回不同的结果。
那么对类来说,我们也需要向它传入不同的参数,以期实例化出的对象不相同,那么我们是否可以直接在实例化时就传入呢:

Student = Student(name = 'wjp',age = 18)

这样做显然是不行的,那么我们需要在类中定义一个’特殊’的函数:

class Student():
    name = ''
    age = 0
    def __init__(self,name,age):
        # 构造函数
        # 初始化对象的属性
        self.name = name
        self.age = age 
    def do_homework(self):
        print('homework')
student1 = Student('石敢当',18)
print(student1.name)

当使用类来实例化一个对象时,py会自动的调用构造函数,同时我们也可以显式的去调用构造函数。显示的调用构造函数返回的是none。
构造函数的作用就是让你的类模板实例化不同的对象,__init__的形参中定义了哪些参数,那么在类实例化中就要传入哪些参数。

区分模块变量与类中的变量

类比下模块中全局变量和局部变量,局部变量的作用域在内部,所以局部变量的值不会改变全局变量。
但如果你对于类中的理解,等同于局部变量,那就不对了。 不能把模块中的全局变量和模块中函数的局部变量等同于类中类变量实例变量

类变量和实例变量

类变量就是和类相关的变量,实例变量是和对象相关联在一起的,而对象是由类这个模板创建出来的。
定义在方法之外,类中的变量,叫做类变量,而实例变量则是通过self.实例变量名来保存变量的特征值。

student1 = Student('石敢当',18)
student2 = Student('喜小乐',19)
# 实例变量
print(student1.name)
print(student2.name)
# 类变量
print(Student.name)

那么定义类变量有什么意义呢?我们从面向对象的角度来思考问题,面向对象是计算机对现实世界中各种关系的一个映射。类是抽象的,对象是具体的,所以name和age两个变量定义为类变量是不合适的,应该定义为实例变量和对象关联起来。
(一定要从现实世界的角度去考虑,为什么要有类,为什么要有类变量,为什么要有对象。)
举个例子来说明类变量的作用以及什么时候要用类变量:

class Student():
    sum = 0

面向对象是一个抽象的概念,需要有一个抽象的思考能力,并且在实践中才能反复使用,才能深刻理解。

类与对象的变量查找顺序

这里有一个问题,同样是上面的类,我想输出实例变量,但是现在输出的是类变量的原因是什么呢?

class Student():
    name = ''
    age = 0
    def __init__(self,name,age):
        name = name
        age = age
student1 = Student('石敢当',18)
# 想打印实例变量
print(student1.name)
# 想打印类变量
print(Student.name)

那么这个问题是为什么呢?我们采用__dict__来打印当前这个对象下的所有相关的变量,得到的是一个空字典,而加了self来赋值的话,对象下的变量字典中才有实例变量。所以说对于输出的实例变量,实质上是空值的,那为什么不输出none,而输出类变量呢?
那么这里就引出了py寻找相关变量的机制:如果我们尝试去访问实例变量的话,那么py首先会在对象的实例变量列表中去查找有没有一个name的变量。如果没有,py并不会返回一个空,它会继续到类变量里中去寻找。
(如果类变量中也没有找到,且存在继承关系的话,那么会继续遍历到其父类中去寻找。)

self与实例方法

问题一:

class Student():
    name = 'wjp'
    age = 23
    def __init__(self,name,age):
        self.name = name
        self.age = age
        print(age)
        print(name)

问题二:
在实例方法中,是否能访问到类变量?

实例方法在定义时,第一个参数需要定义self,但是实例方法在引用时,py会帮我们自动调用self(不需要再显式指定)。同样,在定义时也可以不用self,可以用this来进行替代,可以理解为py之禅中的显胜于隐。
self就是当前调用某一个方法的对象,self只和对象有关,和类是没有关系的。

def __init__(this,name,age):
    this.name = name

实例变量和实例方法类比:
实例变量是与对象相关联的定义的变量,实例方法是和对象实例相关联的,实例可以调用的方法叫做实例方法。(实例方法最大的一个特点就是其参数需要传入一个self)

在实例方法中访问实例变量与类变量

变量间的关系是怎样的

从面向对象的角度上来讲,方法代表了类的一个行为,而变量代表的是刻画了一个类的特征。在绝大多数情况下,方法需要对变量做一定的运算,最终去改变变量的一个状态。

在实例方法中是否能访问到类变量?

前置知识回顾

实例方法的主要作用,是用来描述类的行为,而构造函数的作用是用来初始化类的各种特征
在实例方法中,self.name和name是否等价?self.name读取的是实例变量,而name读取的是形参的name
通过__dict__魔法函数方法来查看对象的使用,但是只能用于类的外部调用中,在外部调用的过程才可以正确输出,内部则不可以。如:

class Student():
    name = ''
    def __init__(self,name1,age):
        self.name = name1
        self.age = age
        print(self.name)
        print(self.__dict__)
        # 下面这句报错
        print(name)

实例方法中访问变量的第一种方法

我们复习下在类的外部如何访问到类变量的方法,在外部访问时,我们通过类.类变量名来对类变量进行访问。那么同样,内部访问时也是通过类.类变量名进行访问。

实例方法中访问变量的第二种方法

self.class.类变量名

class Student():
    sum1 = 0
    def __init__(self, name, age):
        self.name = name 
        self.age = age
        print(self.__class__.sum1)

故一定要理清楚类变量,实例变量,实例方法它们之间相互调用的一个规律。

类方法

为什么会有类方法,以及类方法有什么样的作用;
类方法和实例方法的两点不同:

  1. 定义实例方法需要参数self,定义类方法需要参数cls;(名字不同其实无所谓)
  2. 类方法前需要加上@classmethod;
  3. 实例方法关注的是对象的事物,类方法关注的是事物本身;
   class Student:
   sum = 0
   def __init__(self):
        pass
   def do_homework(self):
      print('homework')
   @classmethod
   def plus_sum(cls):
      cls.sum += 1
      print(cls.sum)d
# 类方法的调用

# 既然是类方法,调用时就和类相关,和对象是没有太大关系的;

Student.plus_sum()

类方法的主要作用是操作一些和类相关的变量,那么有两个问题:

  1. 既然可以在实例方法中操作类变量,那么还要类方法做什么?
  2. 可以用一个类来调用类方法,那么可否用对象来调用类方法?
    1–>就是为了抽象及对应,类的事让类方法去处理;
    2–>py中可以用对象来调用类方法,其他语言则有限制;

静态方法

@staticmethod
def add(x,y):
	# 静态方法访问类变量
    print(Student.sum)
    # 静态方法访问实例变量
    print(name)

静态方法和实例方法、类方法的不同:

  1. 静态方法不像实例方法和类方法一样有一个强制的’关键字’:类方法中的cls代表的是类本身,实例方法中的self代表的是对象本身;
  2. 上方有@staticmethod装饰器;

静态方法和面向对象的关联性非常弱,一般情况下不推荐使用;

成员可见性

成员理解为:变量和方法,其可见性就是py对象下面的变量和方法的可见性。
对于成员,我们有外部访问和内部访问两种途径,实际上使用时,要通过方法对实例变量进行操作,而不是直接修改,因为在方法内可以进行变量修改。

# 想对score赋值,定义方法
def marking(self,score):
	if score < 0:
    	return '不能够给别人打负分'
    self.score = score
    print(self.name + '同学本次考试分数为:' + str(self.score))

私有方法

对类下面的变量的值的修改要通过方法来完成,而不能直接进行修改。
那么py怎么来定义成员的私有或者公开属性,来防止对其直接修改的操作呢?
py中定义,当成员前有双下划线时,就定义了成员是私有的,就不可以在外部进行调用。
如:

# 私有方法
def __marking(self,score):
	pass

那为什么构造函数__init__的前面有双下划线,但是却不是私有的呢?
这样前后都有下划线其实是py的一个自定义函数命名规则,即魔法函数,成员可见性为公开,如:

def __marking__(self,score):
	pass

私有变量

上面我们说了私有方法的定义,而对于私有变量,严格意义上来讲,py是没有私有变量的。我们先来看一个例子:

class Student():
	def __init__(self):
    	self.__score = __score

但是在实操中,我们将方法设置为私有后,私有方法不可访问,但是把变量设置为私有后,变量却可以访问且赋值,这是什么原因呢?

如:

class Student():
    def __init__(self):
        self.__score = 0
    def marking(self, score):
        self.__score = score
        print(self.__score)
student1 = Student('大鹏',23)
student1.marking(100)
student1.__score = 666
print(student1.__score)

student2 = Student('小菜',23)
print(student2.__score)
--- 
大鹏的成绩是100
666
报错....

在这里我们直接将私有变量__score进行了修改为了666,那么这个私有性何在?为什么可以直接修改呢?
事实上,这里之所以能修改是我们看到的’假象’,根据py动态语言的特性,这里student.__score实质上是新创建了一个变量__score,接着对其进行print。而py中私有变量的机制里,本质上是对双下划线定义的变量进行了一次新的命名:_类名__私有变量名,所以直接访问私有变量名时会访问不到。

类的继承

继承性最基本的定义就是为了避免我们定义重复的方法和重复的变量。
在py中,继承某类的方式是在其括号内写上父类,如:

class Human():
	pass
class Student(People):
	pass

接着在考虑继承时,需要考虑子类的特性,对Student类来说,其name,age不是特有的特性,因此可以将其放在父类中,py是可以允许多继承的原则,一个父类是可以有多个子类的:

class Human():
	def __init__(self,name,age):
    	self.name = name
        self.age = age
class Student(Human):
	def __init(self,school,name,age):
    	self.school = school
        # 掉用父类的初始化方法一:
        # 此处要加self:因为其和实例化时去调用构造函数是不一样
        # 通过对象来调用实例方法时,其self知道这个对象,而通		 # 过类来调用时,其实例方法需要指明self;
        Human.__init__(self, name, age)
        # 方法二:
        super(Student, self).__init__(name, age)
	def do_homework(self):
    	pass
student = Student('阿里','大鹏',23)
print(student.name)

猜你喜欢

转载自blog.csdn.net/qq_29027865/article/details/89844083