一、类是什么?对象是什么?
1.类(class):
一个共享相同结构和行为的对象的集合。类(Class)定义了一件事物的抽象特点。通常来说,类定义了事物的属性和它可以做到的(它的行为)。举例来说,“狗”这个类会包含狗的一切基础特征,例如它的孕育、毛皮颜色和吠叫的能力。类可以为程序提供模版和结构。
2.对象(object)
对象,是一个抽象概念,表示任意存在的事物,通常将对象划分为两个部分,即静态部分与动态部分。静态部分被称为“属性”,任何对象都具备自身属性,这些属性不仅是客观存在的,而且是不能被忽视的,如人的性别。动态部分是对象的行为,动态部分被称为“方法”,即对象执行的动作,如人的行走
二者之间的关系?
①类是用来描述具有相同的属性和方法的对象的集合,是对象的属性和方法的载体。
②对象是类的实例。
举个通俗易懂的例子:当我们在图纸上设计出一栋房子模型时,就可以认为这张模型是一个类,当我们照着这个模型在现实中盖出一栋真实的房子时,就可以认为这个房子是这个类(模型)的实例化------即对象
class Model: # 相当于在图纸上设计模型
high = 12
width = 10
area = 120
# 模型设计好了,这个模型的名字叫 Model,它是一个类
fangzi = Model() # 这个操作相当于在现实中盖房子
# 按照 Model这个模型(类)盖了(实例化)一个叫 fangzi 的房子(对象)
二、面向对象编程(OOP)
本章明明是讲 类与对象,为什么要提到面向对象编程呢?
因为Python从设计之初就是一门面向对象的语言,因为面向对象编程是一种编程方式,此编程方式的落地需要使用 “类” 和 “对象” 来实现,所以,面向对象编程其实就是对 “类” 和 “对象” 的使用。
面向对象编程 是最有效的软件编写方法之一。在面向对象编程中,你编写表示现实世界中的事物和情景的类,并基于这些类来创建对象。编写类时,你定义一大类对象都有的通用行为。基于类创建对对象时,每个对象都自动具备这种通用行为,然后可根据需要赋予每个对象独特的个性。使用面向对象编程可模拟现实情景,其逼真 程度达到了令你惊讶的地步。
1.含义
面向对象程序设计(Object Oriented Programming,OOP)是一种计算机编程架构。OOP的一条基本原则是计算机程序由单个能够起到子程序作用的单元或对象组合而成。OOP达到了软件工程的三个主要目标:重用性、灵活性和扩展性。OOP=对象+类+继承+多态+消息,其中核心概念是类和对象。[百度百科]
2.特点
OOP的三大特点(也可以认为是 类 的三大特点): 封装、继承、多态(先不拓展,卖个关子,下节见分晓~)
三、再回到 类与对象
1.类的定义与使用
在Python中,类的定义使用class关键字来实现,紧接着class后面的就是类名,类名的首字母要是大写的(拍婶中类名约定以大写字母开头),冒号下边缩进的代码就是 类的一些内容(属性+方法,);类的使用就是对来实例化来创建对象(也叫类对象,因为万物皆对象,对象千千万,我们可以把自己创建的类,实例化后得到的对象叫做类对象,这样可以区分本来就存在的对象与 我们自己创建的对象)
class Turtle: # 类定义
"""关于类的一个简单例子"""
# 属性
color = 'green'
weight = 10
legs = 4
shell = True
mouth = '大嘴'
# 方法
def climb(self):
print('我正在很努力的向前爬...')
def run(self):
print('我正在飞快的向前跑...')
tt = Turtle() # 类的使用,即类的实例化
tt.climb() # 我正在很努力的向前爬...
2.对象 = 属性 + 方法 (或者先理解 类也是等于属性 + 方法)
由上可知,类的定义里包含了一些内容,即属性和方法,想当然,在实例化一个类对象的时候,也包含了属性和方法(别人有的,你也要有~)
属性:静态特征(一些变量)
方法:动态的动作(一个个函数)
魔法方法:魔法方法也是方法,比如 __init__的这个魔法方法具有初始化的作用,也就是当该类被实例化的时候就会自动执行该函数
3.封装
封装是面向对象编程的核心思想,将对象的属性和行为封装起来,而将对象的属性和行为封装起来的载体就是类,类通常对客户隐藏其实现细节,这就是封装思想。
4.继承:子类自动共享父类之间数据和方法的机制
class MyList(list): # 这里是一种继承,即 MyList 继承了 list 的方法与属性
pass
lst = MyList([1, 5, 2, 7, 8]) # 所以这里的 MyList 相当于 list 来使用
lst.append(9)
lst.sort()
print(lst)
# [1, 2, 5, 7, 8, 9]
5.多态:不同对象对同一方法响应不同的行动(通俗地可理解为在不同的类中,其属性(名)或方法(名)可以相同,而在使用时不会发生混乱)
class People:
def run(self):
print('人正在走')
class Pig:
def run(self):
print('pig is walking')
class Dog:
def run(self):
print('dog is running')
people = People()
pig = Pig()
people.run() # 人正在走
pig.run() # pig is walking
people 和 pig 这是两个不同的对象,在调用相同的方法(run 函数)时,可以正确的找到自己的 run,而不会乱用。
6.self 是什么?
类的方法与普通的函数只有一个特别的区别 —— 它们必须有一个额外的第一个参数名称(对应于该实例,即该对象本身),按照惯例它的名称是 self
。在调用方法时,我们无需明确提供与参数 self
相对应的参数。.
先看例子
class Ball:
def setName(self, name):
self.name = name
print("我叫%s,该死的,谁踢我..." % self.name)
a = Ball()
a.setName("球A") # 我叫球A,该死的,谁踢我...
b = Ball()
b.setName("球B") # 我叫球B,该死的,谁踢我...
c = Ball()
c.setName("球C") # 我叫球C,该死的,谁踢我...
可以发现 setName(self, name) 拥有两个参数,而我们在调用方法时却只输入一个参数,比如 a.setName(“球A”),这个 “球A” 是传给 name 的,那么self 的作用是什么呢? 事实上,Python 的 self
相当于 C++ 的 this
指针。
self 是用来辨认实例对象的(同一个类实例化出来的不同的对象),比如 a.setName(“球A”) 时,self 就知道这个球A参数是对象a的,所以就输出“我叫球A,该死的,谁踢我…”,而 b.setName(“球B”) 时,输出“我叫球B,该死的,谁踢我…”
更通俗地理解:前面说到图纸上设计的房子模型是一个类,然后在现实中照着模型盖一栋房子是这个类的实例对象,那么我们可以照着这个模型(类)盖(实例化)很多个房子(对象),可是每个房子的主人都不一样啊,怕认错房?这是 self 就来了,它说:我就是这些房子的门牌号,你们可以认住我,就不怕进错家了!
7.关于几点
1)在继承中如果子类中定义与父类同名的方法或属性,则会自动覆盖父类对应的方法或属性。
解决方法:
①调用未绑定的父类方法父类名.__init__(self)
②使用super函数super().__init__()
(多重继承都能解决,且不用一个个输入父类名)
2)属性与方法名相同,属性会覆盖方法。
解决方法:
①属性名用名词,方法名用动词
②尽量不要在一个类里面定义出所有想到的属性和方法,应合理使用继承和组合机制进行拓展
3)访问私有变量、私有方法的途径
① 通过内部函数反问
②通过调用 “_类名__私有方法名” 访问
class AA:
__num = 1
def __str(self):
string = 'i love u'
print(string)
def str(self):
return self.__str()
a = AA()
print(a._AA__num) # 1 调用 _类名__私有变量名
a.str() # i love u 内部函数访问
4)组合:就是把类的实例化,放到一个新类当中去
class Turtle:
def __init__(self, x):
self.num = x
class Fish:
def __init__(self, x):
self.num = x
class Pool:
def __init__(self, x, y):
self.turtle = Turtle(x) # 实例化一个Turtle 传给 turtle
self.fish = Fish(y)
def print_num(self):
print("水池里面有乌龟%s只,小鱼%s条" % (self.turtle.num, self.fish.num))
# self.turtle 是一个类
p = Pool(2, 3)
p.print_num()
# 水池里面有乌龟2只,小鱼3条
5)类属性与实例属性
类属性:类里面方法外面定义的变量称为类属性。类属性所属于类对象并且多个实例对象之间共享同一个类属性,说白了就是类属性所有的通过该类实例化的对象都能共享。
【例子】
class A():
a = 0 # 类属性
def __init__(self, xx):
# 使用类属性可以通过 (类名.类属性)调用。
A.a = xx
实例属性:实例属性和具体的某个实例对象有关系,并且一个实例对象和另外一个实例对象是不共享属性的,说白了实例属性只能在自己的对象里面使用,其他的对象不能直接使用,因为self
是谁调用,它的值就属于该对象。
【例子】
class 类名():
def __init__(self):
self.name = xx #实例属性
类属性和实例属性区别
- 类属性:类外面,可以通过
实例对象.类属性
和类名.类属性
进行调用。类里面,通过self.类属性
和类名.类属性
进行调用。 - 实例属性 :类外面,可以通过
实例对象.实例属性
调用。类里面,通过self.实例属性
调用。 - 实例属性就相当于局部变量。出了这个类或者这个类的实例对象,就没有作用了。
- 类属性就相当于类里面的全局变量,可以和这个类的所有实例对象共享。
※当实例属性和类属性使用相同的名字,实例属性会将类属性屏蔽掉生成一个新的与该类属性名字一样的实例属性,此外属性与方法名相同,属性也会覆盖方法。
# 创建类对象
class Test(object):
class_attr = 100 # 类属性
def __init__(self):
self.sl_attr = 100 # 实例属性
def func(self):
print('类对象.类属性的值:', Test.class_attr) # 调用类属性
print('self.类属性的值', self.class_attr) # 相当于把类属性变成实例属性
print('self.实例属性的值', self.sl_attr) # 调用实例属性
a = Test()
a.func()
# 类对象.类属性的值: 100
# self.类属性的值 100
# self.实例属性的值 100
b = Test()
b.func()
# 类对象.类属性的值: 100
# self.类属性的值 100
# self.实例属性的值 100
a.class_attr = 200 # 此处对 class_attr 相当于 在 a 中生成了一个新的名为class_attr属性
a.sl_attr = 200 #
a.func()
# 类对象.类属性的值: 100
# self.类属性的值 200
# self.实例属性的值 200
b.func()
# 类对象.类属性的值: 100
# self.类属性的值 100
# self.实例属性的值 100
Test.class_attr = 300
a.func()
# 类对象.类属性的值: 300
# self.类属性的值 200
# self.实例属性的值 200
b.func()
# 类对象.类属性的值: 300
# self.类属性的值 300
# self.实例属性的值 100
解决方法:
①属性名用名词,方法名用动词
②尽量不要在一个类里面定义出所有想到的属性和方法,应合理使用继承和组合机制进行拓展
8.什么是绑定?
.
到底什么是绑定,是这样吗?
.
严肃分割线
Python 严格要求方法需要有实例才能被调用,这种限制其实就是 Python 所谓的绑定概念。(注意关键词,绑定 = 调用方法时需要有实例)
看例子
class A:
def printA(): # 此处故意不填self
print('i love u')
A.printA() # i love u
# 注意此处不是调用
这样做似乎没有问题,但这样做,根据类实例化后的对象根本无法调用里边的函数
class A:
def printA():
print('i love u')
a = A()
a.printA() # TypeError: printA() takes 0 positional arguments but 1 was given
实际上由于Python 的绑定机制,这里自动把a (实例对象)
作为第一个参数传入给self,没了self,所以才会出现TypeError。即没有实例对象绑定,方法不能被调用! 加上self 这样就没有问题了
class A:
def printA(self):
print('i love u')
a = A()
a.printA() # i love u
Python 对象的数据属性通常存储在名为.__ dict__
的字典中,我们可以直接访问__dict__
class CC:
def setXY(self, x, y):
self.x = x
self.y = y
def printXY(self):
print(self.x, self.y)
dd = CC()
print(dd.__dict__) # 查看对象的属性
# {}
print(vars(dd))
# {}
print(CC.__dict__)
# {'__module__': '__main__', 'setXY': <function CC.setXY at 0x000000C3473DA048>, 'printXY': <function CC.printXY at 0x000000C3473C4F28>, '__dict__': <attribute '__dict__' of 'CC' objects>, '__weakref__': <attribute '__weakref__' of 'CC' objects>, '__doc__': None}
dd.setXY(4, 5)
print(dd.__dict__) # 这就有了,归功于self的指针作用,这个属性是实例对象特有的
# {'x': 4, 'y': 5}
9.一些相关的内置函数(BIF)
习题
部分练习题与解答:
1、以下类定义中哪些是类属性,哪些是实例属性?
class C:
num = 0 # 类属性
def __init__(self):
self.x = 4 # 实例属性
self.y = 5 # 实例属性
C.count = 6 # 类属性
2、怎么定义私有方法?
答:在定义方法时,在方法名前加上“__”两个下划线,那么这个方法就会变为私有方法
3、尝试执行以下代码,并解释错误原因:
class C:
def myFun():
print('Hello!')
c = C()
c.myFun()
答:第一个错误,定义方法时,没有使用必要参数 self;第二个错误,错误的使用了缩进
class C:
def myFun():
print('Hello!')
c = C()
c.myFun()
# NameError: name 'C' is not defined
修正后:
class C:
def myFun(self):
print('Hello!')
c = C()
c.myFun() # Hello!
4、按照以下要求定义一个游乐园门票的类,并尝试计算2个成人+1个小孩平日票价。
要求:
-
平日票价100元
周末票价为平日的120%
-
儿童票半价
class Ticket:
def __init__(self, adult, child):
self.a_num = adult
self.c_num = child
def normal_day(self):
money = self.a_num*100 + self.c_num*100*0.5
return money
def weekend_day(self):
money = 1.2*(self.a_num*100 + self.c_num*100*0.5)
return money
ticket_money = Ticket(2, 1)
print(ticket_money.normal_day()) # 250.0