面向对象基础

面向对象思想

# 面向过程的核心是:过程
- 过程的终极奥义就是将程序流程化
- 过程是"流水线",用来分步骤解决问题

# 面向对象的核心是:对象
- 对象的终极奥义就是将程序"整合"
- "整合"程序中的数据和功能;对象是"容器",用来盛放数据与功能

# 许多个对象具有类似的数据和功能,那这些对象是类似的。可以将他们相同的数据和功能再次"整个"到一起
# 类也是"容器",该容器用来存放同类对象共有的数据与功能

我们知道程序=数据结构+算法。这是比较官方比较抽象的说法。其实,本质上程序就是一堆数据和一些计算这些数据的函数方法(功能)。

在此之前我们学习python的基本数据结构和函数,其实就是分别学习数据和方法。

我们的程序想要具有非常好的扩展性,就必须高度整合到一起,用函数可以实现部分程序的"整合"。现在我们学习面向对象的编程思想,就是一种高度"整和"数据和功能的编程思想。


类的定义&类属性访问

现实生活中是先有了对象,再抽象出不同的类。但是程序在编写过程中却是先有类再有对象。

定义类

类是对象相似的数据与功能的集合体。所以类体中最常见的是变量与函数的定义,但类内部是可以包含任意代码的。
注意:类体内代码是在类定义阶段就会立即执行,会产生类的名称空间。

# 定义类,类名采用驼峰体命名
class Student:
    
    # 1、变量的定义
    stu_school = 'oldboy'

    # 2、功能的定义
    def tell_stu_info(stu_obj):
        print('学生信息:名字:%s 年龄:%s 性别:%s' %(
            stu_obj['stu_name'],
            stu_obj['stu_age'],
            stu_obj['stu_gender']
        ))

    def set_info(stu_obj,x,y,z):
        stu_obj['stu_name'] = x
        stu_obj['stu_age'] = y
        stu_obj['stu_gender'] = z

类属性访问

上面的Student类,我们定义了一个数据属性和两个函数属性,可以通过两种方式访问这三个类属性。

# 方式1:类的__dict__内置方法,返回一个字典
print(Student.__dict__)		# 打印Student类的所有属性,字典键值对的格式,__dict__字典存放的是共有的属性
# 可以像字典一样通过键访问其中的值
print(Student.__dict__['stu_school'])		# oldboy
print(Student.__dict__['tell_stu_info'])	# <function Student.tell_stu_info at 0x000001BD0AAE7310>
print(Student.__dict__['set_info'])			# <function Student.set_info at 0x000001BD0AAE71F0>

# 方式2:类名.属性(推荐)
print(Student.stu_school) 		# 本质是方式1的操作
print(Student.set_info)

# 添加类属性
Student.x = 1111 				# Student.__dict__['x'] = 111
print(Student.__dict__)			# 将增加一个键值对

实例化对象&对象属性

类名加括号就可以调用类,调用类的目的是创建对象。对象的创建又称类的实例化,即实例出一个对象。

先定义类然后才能调用类产生对象。

# 简单实例对象
stu1_obj = Student()
stu2_obj = Student()
stu3_obj = Student()

# 查看对象的属性。这种方式刚实例化,每个对象是没有自己特有的属性,多一打印的结果是空的。
# 也就是说对象的.__dict__中存放的是对象的私有属性,不是类中大家共有的。
print(stu1_obj.__dict__)
print(stu2_obj.__dict__)
print(stu3_obj.__dict__)

# 通过stu1_obj.__dict__['name'] = 'egon',可以添加对象的私有属性,但这种方式不用想就很繁琐。

我们期望的是实例化对象的时候自动添加对象的自己的属性。

此时可以在类中定义一个函数,在类实例化的时候自动帮我们添加对象的属性。

# 高级实例对象
class Student:
    
    stu_school='oldboy'

    # 空对象,'egon',18,'male'
    def __init__(obj,x,y,z):
        obj.stu_name = x # 空对象.stu_name = 'egon'
        obj.stu_age = y  # 空对象.stu_age = 18
        obj.stu_gender = z # 空对象.stu_gender = 'male'
       

# 实例化对象
stu1_obj=Student('egon', 18, 'male') 	# Student.__init__(空对象,'egon',18,'male')
stu2_obj=Student('lili', 19, 'female')
stu3_obj=Student('jack', 20, 'male')

print(stu1_obj.__dict__)
print(stu2_obj.__dict__)
print(stu3_obj.__dict__)

# 输出:每个对象的__dict__都存放自己的属性
{'stu_name': 'egon', 'stu_age': 18, 'stu_gender': 'male'}
{'stu_name': 'lili', 'stu_age': 19, 'stu_gender': 'female'}
{'stu_name': 'jack', 'stu_age': 20, 'stu_gender': 'male'}

调用类的过程又称之为实例化,发生三件事:

  • 先产生一个空对象
  • python会自动调用类中的__init__方法然将空对象以及调用类时括号内传入的参数一同传给__init__方法
  • 返回初始完成后的对象,即实例化后的对象

实例化对象之后,对象的属性访问和类的属性访问一样:

  • 方式1:__dict__字典
  • 方式二:对象.属性

总结__init__方法

1、会在调用类时自动触发执行,用来为对象初始化自己独有的数据
2、__init__内应该存放是为对象初始化属性的功能,但是可以存放任意其他代码,想要在类调用时就立刻执行的代码都可以放到该方法内
3、__init__方法必须返回None,默认不写return语句

对象属性查找顺序

类中存放的是对象共有的数据和功能,类可以访问类内存放的任意数据和功能

print(Student.stu_school)		# oldboy
print(Student.tell_stu_info)	# <function Student.tell_stu_info at 0x0000014A2DEE75E0>

此外我们还知道每个对象都有其自己的属性,别人访问不到。对象的属性字典(__dict__)是没有stu_school属性的。那为什么可以通过对象.属性的方式访问到类的属性呢?

print(id(Student.stu_school))	# 大家的id地址都一样

print(id(stu1_obj.stu_school))
print(id(stu2_obj.stu_school))
print(id(stu3_obj.stu_school))

这是因为类中的东西本质是给对象用的,且大家访问到的属性的地址是相同的。这就涉及到对象属性访问顺序了。

如果对象有这个属性即用对象自己的,如果对象没有就从类里面找,找到就用找不到就报错。

如果对象都没有添加自己的stu_school属性,那大家访问到的都是类中的,所以他们的id地址是相同的

但如果某个对象偷偷添加了自己的stu_school,那的对象是不会变的。再次访问时,有这个属性的对象就用自己的,没有就用类的。

stu1_obj.stu_school = 'OLDBOY'	# 对象1添加了自己的stu_school属性
print(stu1_obj.stu_school)		# OLDBOY		

print(Student.stu_school)		# oldboy	类的不变
print(stu2_obj.stu_school)		# oldboy	别人对象没有自己的还是访问类的
print(stu3_obj.stu_school)		# oldboy

绑定方法

class Student:
   
    stu_school='oldboy'
 
    def __init__(self,x,y,z):	# 此处self可以是任意符号,但大家约定俗成使用的是self
        self.stu_name = x 
        self.stu_age = y 
        self.stu_gender = z 
    
    def tell_stu_info(self):
        print('学生信息:名字:%s 年龄:%s 性别:%s' %(
            self.stu_name,
            self.stu_age,
            self.stu_gender
        ))

    def set_info(self,x,y,z):
        self.stu_name = x
        self.stu_age = y
        self.stu_gender = z
        
    def choose(self,x):
        print('正在选课')
        self.course = x

        
# 实例化三个对象
stu1_obj=Student('egon',18,'male') 
stu2_obj=Student('lili',19,'female')
stu3_obj=Student('jack',20,'male')

类中定义的函数主要是给对象使用的,而且是绑定给对象的,虽然所有对象指向的都是相同的功能,但是绑定到不同的对象就是不同的绑定方法,内存地址各不相同。

print(Student.tell_stu_info)
print(stu1_obj.tell_stu_info)
print(stu2_obj.tell_stu_info)
print(stu3_obj.tell_stu_info)
# output:
<function Student.tell_stu_info at 0x000001E9A8C47670>
<bound method Student.tell_stu_info of <__main__.Student object at 0x000001E9A8C33B50>>
<bound method Student.tell_stu_info of <__main__.Student object at 0x000001E9A8C3E0D0>>
<bound method Student.tell_stu_info of <__main__.Student object at 0x000001E9A8C5AF40>>

绑定方法的特殊之处在于:谁来调用绑定方法就会将谁当做第一个参数自动传入。

stu1_obj.tell_stu_info() 	# tell_stu_info(stu1_obj)
stu2_obj.tell_stu_info() 	# tell_stu_info(stu2_obj)
stu3_obj.tell_stu_info() 	# tell_stu_info(stu3_obj)
# output:
学生信息:名字:egon 年龄:18 性别:male
学生信息:名字:lili 年龄:19 性别:female
学生信息:名字:jack 年龄:20 性别:male

类也可以效用自己的函数属性。但是由于这些函数是绑定给对象使用的,类使用的时候不是自动传第一个参数,需要手动传第一个参数。

Student.tell_stu_info(stu1_obj)
Student.tell_stu_info(stu2_obj)
Student.tell_stu_info(stu3_obj)
Student.set_info(stu1_obj, 'EGON', 19, 'MALE')

总结

# 类内部定义的数据属性和函数属性全部存在类的__dict__字典里面,可以通过字典的增删改查修改类这些属性
# 对象的__dict__字典里面存放的是对象的特有属性,也可以通过这个字典来增删改查属性
# 简单点,不管是类还是对象都可以通过 .属性 的方式增删改查__dict__里面的属性,即修改共有的属性和特有的属性
# 一个对象访问某个属性时,优先访问自己__dict__字典里面的属性,没有的话再访问类的__dict__字典里面的属性
# 类里面的函数属性时绑定给对象使用的,对象使用绑定方法时,自动将对象作为第一个参数传给这个绑定方法
# 类当然也可以使用类内定义的函数,不过此时这个函数就是普通函数,类使用需要手动传第一个参数,即传一个对象进去
posted @ 2020-04-07 16:42  the3times  阅读(139)  评论(0编辑  收藏  举报