面向对象基础
编程思想
指的是用计算机来解决人类实际问题的思维方式。
面向过程编程思想
核心是”过程“二字,过程即流程,指的是做事的步骤。
优点:
- 复杂的问题流程化,进而简单化
缺点:
- 扩展性非常差
面向过程的编程思想应用场景解析:
1、不是所有程序都需要频繁更迭,比如脚本文件。
2、即便一个软件需要频繁更迭,也并不代表这个软件所有的组成部分都需要一起更迭。
函数式编程
函数式编程并非用函数编程那么简单,而是将计算机的运算视为数学意义上的运算,比起面向过程,函数式更加注重的是执行结果而非执行的过程。
Python并不是一门函数式编程语言,但是仍提供了很多函数式编程的特性,如lamda,map,reduce,filter。
面向对象编程(OOP)
在现实生活中,对象指的是某个具体的事物,然后根据多个对象的相同属性归纳出类。例如四条腿、长毛、喵喵叫的是猫类,两条腿、头会秃、会说脏话的是人类。具体到某一个个体就是对象,比如你家养的肥猫和你到现在也没找到的女朋友,这些就是属于类中的一个具体对象。
面向对象编程的核心是对象二字,对象是特征与技能的结合体。
基于面向对象编程的思想编写程序,就好比是在创造一个世界,你就是这个世界的上帝,是一种上帝式的思维方式。
- 优点:可扩展性强。
- 缺点:编程的复杂度要高于面向过程。
在计算机中,则是先定义出类,类相当于一个容器,这个容器可以用来存放属性(变量)与方法(函数)。面向对象编程就是通过类实例化出一个个具体的对象,把原本分散开的相关数据与功能整合到一个个的对象里,这么做既方便使用,也可以提高程序的解耦合程度,进而提升了程序的可扩展性。需要强调的是,软件质量属性包含很多方面,面向对象解决的仅仅只是扩展性问题。
类与对象
类即类别/种类,是面向对象分析和设计的基石,如果多个对象有相似的数据与功能,那么该多个对象就属于同一种类。有了类的好处是:我们可以把同一类对象相同的数据与功能存放到类里,而无需每个对象都重复存一份,这样每个对象里只需存自己独有的数据即可,极大地节省了空间。所以,如果说对象是用来存放数据与功能的容器,那么类则是用来存放多个对象相同的数据与功能的容器。
需要强调的是:在程序中,必须要事先定义类,然后再调用类产生对象(调用类拿到的返回值就是对象)。产生对象的类与对象之间存在关联,这种关联指的是:对象可以访问到类中共有的数据与功能,所以类中的内容仍然是属于对象的,类只不过是一种节省空间、减少代码冗余的机制,面向对象编程最终的核心仍然是去使用对象。
类的定义
面向对象的基本思路就是把程序中要用到的、相关联的数据与功能整合到对象里,然后再去使用。
在Python中定义类的语法:
class 类名:
variable1 = 'a'
variable2 = 'b'
def method(self):
pass
类的命名应该使用"驼峰体",类体最常见的是变量的定义和函数的定义,但其实类体可以包含任意Python代码,类体的代码在类定义阶段就会执行,因而会产生新的名称空间用来存放类中定义的名字,可以打印类名.__dict__
来查看类这个容器内命名空间盛放的东西。
class People:
a = 10
b = [1,2,3]
def m1(self):
pass
print(People.__dict__)
结果为一个字典:
{'__module__': '__main__', 'a': 10, 'b': [1, 2, 3], 'm1': <function people.m1 at 0x0000000002769700>, '__dict__': <attribute '__dict__' of 'people' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None}
那么我们通过字典的key取出里面的a属性,:
print(People.__dict__['a'])
10
但Python提供了专门的属性访问语法:
print(People.a) # 等同于print(People.__dict__['a'])
除了查看属性,也能修改或删除属性:
People.a += 10
print(People.a)
20
del People.a
实例化
调用类的过程称为将类实例化,得到的返回值就是程序中的对象,或称为一个实例:
# 加括号调用
libai = People()
print(libai)
<__main__.People object at 0x0000000001E44BB0>
一个类可以实例化出多个对象:
dufu = People()
wangwei = People()
print(dufu)
print(wangwei)
<__main__.People object at 0x0000000001E24BB0>
<__main__.People object at 0x0000000001E24B50>
类实例化出对象后,对象有自己的名称空间:
print(dufu.__dict__)
{}
给对象赋值
类中定义的属性是共享给所有对象用的,指向相同的内存地址。
print(id(libai.a))
print(id(wangwei.a))
8791138305984
8791138305984
现在要给对象赋予专有的属性:
libai.a = 'hello'
print(libai.a)
hello
给对象赋值影响的是对象的名称空间,不会影响类的名称空间。
修改类的属性
类的属性修改只会影响类名称空间,不会影响对象的名称空间。
class Boy:
head = 1
print(Boy.head) # 1
Boy.head = 2
libai = Boy()
libai.head = 3
print(libai.head) # 3
类的属性修改后,对象在取值时,取的即是修改后的值。
对象的取值顺序
对象的名称空间里只存放对象独有的属性,对象在访问属性时,会先从对象本身的名称空间中查找,找不到则去类的名称空间查找,类若没有则会报错。
print(libai.a)
10
print(dufu.a) # 对象没有则向类找.
20
类中定义的方法
在类中定义的函数也就是方法,主要是绑定给对象使用的,虽然也可以通过类名来调用类之内的方法,但这样使用它仅为一个普通函数,同样遵循函数的语法,有多少个形参则要传入多少个实参,也能有默认形参。
通过类名调用方法时:
class people:
def m1(x):
print(x)
people.m1('哈哈哈')
哈哈哈
在类中定义方法时,第一个位置形参有特殊作用,通过对象调用时,该方法就是对象的绑定方法。在调用时,会将对象本身当做实参传给该形参。约定俗成该形参为self,实际上也可以用其他名称。
通过对象调用时:
class people:
x = 10
def m1(self):
print(f'这是self {self}')
libai = people()
libai.m1() # 相当于 libai.m1(libai)
print(f'这是libai{libai}')
这是self <__main__.people object at 0x0000000002564BB0>
这是libai<__main__.people object at 0x0000000002564BB0>
由于通过对象调用类中方法时,会将对象自身当作实参传过去,所以一定要有个形参接收才行,否则会报错。
__init__方法
如果要在实例化时给对象赋予一些初始值,就可以使用__init__方法。在类实例化得到对象时,会触发__init__的执行。
之前给对象赋值:
class Boy:
head = 1
libai = Boy()
libai.age = 18
print(libai.age) # 18
使用__init__方法,将要赋予的初始值当成参数传进方法中:
class Boy:
def __init__(self, age, height):
self.age = age # 前面age是属性名,后面这个age是变量名,接收传给age的参数。
self.height = height
libai = Boy(18,190)
print(libai.age)
print(libai.height)
18
190
约定俗成__init__
方法通常写为第一个方法。
类即类型
在Python3中统一了类与类型的概念,类就是类型。变量有三个指标:id、类型、值。我们用type()函数查看类型时,实际查看的就是它是什么类。
Python2中:
>>> a = 10
>>> type(a)
<type 'int'>
Python3中:
>>> a = 10
>>> type(a)
<class 'int'>
list的append方法原理
# Python内置类可以通过这种方式直接实例化对象,我们自己定义的类则要通过类名加括号的方式构建。
l1 = [1,2,3]
# 通过对象调用绑定方法.
l1.append(4)
# 通过类来调用方法则为普通函数,有几个形参则要传几个实参。
list.append(l1,5)
print(l1)
[1, 2, 3, 4, 5]