面向对象最重要的概念就是(Class)和实例(Instance)有些人也将实例称之为对象对象实例个人认为是同一个意思。

对象:个人理解对象就是一个具体的事物,比如,一名学生、一张桌子、一张凳子、一个笔记本都可以看成是一个对象。

类:个人理解是对对象的一个抽象化,如,定义一个Students类,类里面有nameagesex等,那么通过这个类可以创建出许多个不同的实例

{'name':'小米','age':20,'sex':'男'}
{'name':'小王','age':10,'sex':'女'}
{'name':'小黑','age':30,'sex':'女'}

这三个对象都有不同的名称年龄性别。但是他们有相同的属性nameagesex

1.类的定义

python中类定义是用关键字class定义,类名的首字母要大写。类最基本的功能就是封装。

class Students():
  pass

class StudentHomework():
  pass

最简单的类

class Student():
  name = '小米'
  age = 0
  
  def do_homework(self):
    print('正在做家庭作业')

#实例化类
student1 = Student() 

#调用类的方法
student1.do_homework()

以上大概就是创建一个类并实例化

2.函数与方法的区别

python中定义函数方法都是通过关键字def来定义的,在类中定义的函数习惯性的称之为方法。方法是倾向于设计层面而函数是倾向于面向过程。两者更多是概念上的区分。

函数:函数是可重复使用的,用来实现单一或相关联功能的代码段,函数是一个面向过程的编程。

方法:在类中定义的函数习惯性称之为方法,方法是设计层面的一个概念。

3.类和对象的关系

类是现实世界或思维世界中的实体在计算机中的反映,它将数据以及这些数据上的操作封装在一起。个人理解:类是对象的一个抽象,对象就是实例化类。

设计一个类是否优美,取决于设计者能否抓住类的特征(类变量) 与 行为(方法)

类和对象的关系就是实例化

#实例化
student1 = Student()

4.构造函数

__init__()方法是类的一种特殊的方法,被称为类的构造函数初始化方法,当创建类的实例时就会自动调用该方法。构造方法一般用于初始化。

class Student():
  name = '小米'
  age = 0

  #构造函数
  def __init__(self,name,age):
    self.name = name
    slef.age = age
    print('创建一个名为:' + self.name + '的同学,年龄是:' + str(self.age))
  #方法
  def do_homework(self):
    print('正在做家庭作业')

 #实例化
student1 = Student('小名',20)  

注意:类中的方法需要传self,self名称是官方建议的,但不是绝对的

构造函数和方法区别:

5.区别类变量和实例变量

实例变量用于每个实例的唯一数据,而类变量用于类的所有实例共享的属性和方法:

class Dog:
  #类变量
  kind = 'canine' 

  def __init__(self,name):
    #实例变量
    self.name = name

>>> d = Dog('fido')
>>> e = Dog('buddy')
>>> d.kind
'canine'

>>> e.kind
'canine'

>>> d.name
'fido'

>>> e.name
'buddy'

类变量:类变量是和类关联在一起的变量,类变量放在类下面,如上面kind就是一个类变量

实例变量:实例变量是和实例关联在一起的变量,通过self将实例和变量关联起来。如上面name就是一个实例变量

6.类与对象的变量查找顺序

在Python程序中创建、改变、查找变量名时,都是在一个保存变量名的空间中进行,我们称之为命名空间,也被称之为作用域。python的作用域是静态的,在源代码中变量名被赋值的位置决定了该变量能被访问的范围。即Python变量的作用域由变量所在源代码中的位置决定。变量查找顺序,局部变量->全局变量->内置变量

只有当变量在Module(模块)、Class(类)、def(函数)中定义的时候,才会有作用域的概念。

python在类中查找变量的顺序:实例变量->类变量->父类变量

class Student:
  name = '小三'

  def __init__(self,name):
    name = name

>>> student1 = Student('小王')
>>> print(student1.name)
'小三'

上面代码中,我们只定义Student类变量name,没有定义实例变量,当创建student1实例时,我们尝试能不能打印出实例变量name。结果输出了小三。其实student1实例中并没有name变量,当我们打印实例变量name时,python在实例中找不到变量就会在类中查找变量name,如果找到就返回,找不到就会向父类查找。

为了验证student1实例中真的没有name变量,我们打印一下实例的所有变量

>>> print(student1.__dict__)
{}

我们将name修改为实例变量

class Student:
  name = '小三' #类变量

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

>>> student1 = Student('小王')
>>> print(student1.name)
'小王'

>>> print(student1.__dict__)
{name:'小王'}

7.self与实例方法

在类下面定义一个方法,这个方法如果是实例方法,那么需要在该方法的参数第一个放上固定的self(其他名称也行,官方建议是self),self是python官方固定的,它和其它自定义参数不同,在实例化时,python会将实例传进去,在调用时不需要传递,官方默认帮我们传递了。

在其他语言中,其实也存在self,只不过是隐式的,但是在python中是必须显式声明的,这点开发者需要注意。

//js
class Student{
  //构造方法
  constructor(){
    this.name = '小三'
  }
  
  doHomework(){
    print(this.name + '在做家庭作业')
  }
}

这里的this不需要去声明,因为js中自动帮我们声明了,python中必须要手动声明self,self就是指的是该实例自身

8.实例中访问类变量

在实例方法中访问实例变量是通过self.变量名来访问变量。那么如何在实例中如何访问类变量呢?

通过self.class.[变量名]访问类变量

class Student:
  name = '小三'

  def do_homework(self):
    print('名称是:' + self.__class__.name)

>>> d1 = Student()
>>> d1.do_homework()
'名称是:小三'

通过类名访问类变量

class Student:
  name = '小三'
  
  def do_homework(self):
    print('名称是:' + Student.name)

>>> d1 = Student()
>>> d1.do_homework()
'名称是:小三'

强烈建议:通过方法来访问类变量,不要通过类来操作类变量。

9.类方法

类方法主要是用来描述类的行为,类方法定义

class Student:
  sum = 0 

  #构造行数
  def __init__(self):
    print('构造函数')

  #类方法
  @classmethod  #装饰器
  def plus_sum(cls):
    pass

在实例方法中传入第一个参数self,而在类方法中传入的第一个参数cls。区分类方法和实例方法是看方法上面有没有@classmethod

类方法操作类变量

在类方法中直接可以通过cls来读取类变量,而在实例方法中需要self.__class__来读取变量

class Student:
  sum = 0 

  def __init__(self):
    pass
  
  @classmethod
  def plus_sum(cls):
    cls.sum += 1

>>> d1 = Student()
# 通过实例调用类方法
>>> d1.plus_sum()
# 通过类调用类方法
>>> Student.plus_sum()

10.静态方法

类方法的定义,静态方法不需要传入一个固定名称的参数,静态方法需要用@staticmethod来声明是静态方法

class Student:
  sum = 0 

  def __init__(self):
    pass

  @staticmethod
  def add():
    # 静态方法 对类变量的读取
    print(Student.sum)
    print('这是一个静态方法')

>>> d1 = Student()
# 实例调用静态方法
d1.add()

# 类调用静态方法
Student.add()

类的静态方法可以被实例或类调用

11.私有性

成员的可见性是指变量或方法是否能被访问,类的变量或方法都存在外部访问和内部访问。私有变量只能在实例中访问。

外部访问:指在类的外部调用,内部访问:指在类的内部调用。

其他语言中,成员的可见性是通过明显的标记来标明,如java中就是通过publicprivate标明方法或变量是公开的私有的。在python类中,表示一个实例或 变量公开还是私有是用双下划线(__)开头的。如果一个实例方法或变量不是以双下划线开头的,那么就表示这个实例或变量是公开的,反之是私有的。

class Student:

  #构造方法
  def __init__(self,name,age):
    self.name = name
    self.age = age
    
    #私有变量
    self.__sex = '男'
  #私有方法
  def __get_name(self):
    print(self.name +"性别是:" + self.__sex)

  #公开的
  def do_homework(self):
    print(self.name + "在做家庭作业")

>>> d1 = Student('小王',19)
>>> print(d1.name)
'小王'

>>> print(d1.__sex)
'会报错,因为__sex变量是私有变量只能在内部访问'

>>> print(d1.do_homework())
'小王在做家庭作业'

>>> print(d1.__get_name())
'会报错'

__init__构造方法为什么会公开的呢?

上面代码我们在方法或者变量前面加了__使其成员的可见性变为私有。那么可能有小伙伴们想到__init__构造函数前面不是也有__,为什么构造函数是公开的呢?

小菜在介绍构造函数时说构造函数是一种特殊的方法。细心的小伙伴们就注意到__init__构造函数不仅开头有__,结尾也有__。在python中,默认以__开头方法或变量都会认为是私有的。但是有一种是除外的。

注意:python不会认为以双划线开头 并且 以双下划线结尾的变量 或 方法是私有的。构造函数就属于这种,所以构造函数就不是私有的

通过实例添加变量

python中可以通过实例添加变量

class Student:

  def __init__(self,name,age)
    self.name = name
    self.age = age
    self.__score = 69

>>> d1 = Student('小王',19)
>>> d1.__score = 70
>>> print(d1.__score)
70

如果小伙们读了前面介绍的私有性,再看这段代码,肯定心里一万个cnm在崩腾,不是说私有变量不能在外部被访问吗?怎么现在居然可以赋值?请听小菜一本正经的瞎说:这段代码中d1.__score = 70最后打印出了d1.__score为70。其实这里的__score不是私有变量,而__init__构造函数的__score是私有变量。估计有小伙伴们要懵逼了。

为了演示python是如何操作的,小菜简化代码

class Student:

  def __init__(self,name,age)
    self.name = name
    self.age = age
    self.__score = 69

>>> d1 = Studnet('小王',19)
>>> print(d1.__score)  
'这条语句会报错,原因__score是 私有变量'

>>> print(d1.__dict__)
{'name': '小王', 'age': 19, '_Student__score': 69}

这里我们看到多了一个_Student__score,这是为什么呢?在实例化时,python会将私有变量转换成_Student后面再加变量名。所以当我们在输出d1.__score时就会报错,报一个__score变量不存在。

回过头我们在说这段代码

class Student:

  def __init__(self,name,age)
    self.name = name
    self.age = age
    self.__score = 69

>>> d1 = Student('小王',19)
>>> d1.__score = 70
>>> print(d1.__score)
70

>>> print(d1.__dict__)
{'name': '小王', 'age': 19, '_Student__score': 69, '__score':70}

通过d1.__dict__打印出来实例的所有变量,我们可以分析出d1.__score=70就是给实例定义了一个__score变量,但__score不是私有的,在构造函数中的__score已经在实例化时被转换成_Student__score

12.python的访问私有变量

在上面我们谈了python的私有性,实际上在实例化时将原来的私有变量做了一层命名空间的转换。那么我们如何访问私有变量呢?

class Student:

  def __init__(self,name,age)
    self.name = name
    self.age = age
    self.__score = 69

>>> d1 = Student('小王',19)
>>> print(d1._Student__score)
69

13.继承

类的继承最基本的作用就是为了避免定义重复的变量或方法。

文件结构

./
|-hu.py
|-st.py

hu.py

class Human():
  sum = 0

  def __init__(self,name,age):
    self.name = name
    self.age  = age
  
  def get_name(self):
    print(self.name)

st.py

from hu import Human

class Student(Human):

  def do_homework(self):
    print('调用Student类的方法')

#子类调用父类的sum
>>> print(Student.sum)  
0
>>> d1 = Student('小王',19)
>>> print(d1.name)  #实例调用name
'小王'
>>> print(d1.get_name())  #实例调用父类的方法
'小王'

14.子类方法调用父类方法

./
|-hu.py
|-st.py

hu.py

class Human():
  sum = 0

  def __init__(self,name,age):
    self.name = name
    self.age  = age
    print("父类初始化方法",name, age)
  
  def get_name(self):
    print(self.name)

st.py

from hu import Human

class Student(Human):

  def __init__(self, school, name,age):
    self.school = school
    #父类初始化
    #Human.__init__(self,name,age) 
    #通过 super关键字调用父类
    super().__init__(name,age)

  def do_homework(self):
    print('调用Student类的方法')
  
stu = Student('上海大学','wali',18)

15.总结

ssl

python3.7 入门教程

Python 准备工作(1) Python 基本数据类型 Number(2) Python 字符串 str(3) Python 列表List(4) Python 元组tuple(5) Python 集合set(6) Python 字典 dict(7) Python 变量(8) Python 运算符(9) Python 条件语句(10) Python 循环语句(11) Python 包 模块(12) Python 函数一(13) Python 函数二(14) Python 面向对象(15) Python 正则(16) Python json(17) Python 枚举(18) Python 闭包(19) Python 装饰器(20) Python 杂记(21) Python with(22)