11.2 对象 = 属性 + 方法
一个对象的特征称为"属性",一个对象的行为称为“方法”。
如果把乌龟写成代码,将会是下边这样:
#p11_1.py
class Turtle
: #特征的描述就是属性,在代码层面来看就是变量
#
python用class定义类,class是一个关键字,告诉系统我们要定义一个类
#class后面加一个空格然后加类名
#类名规则:首字母大写,如果多个单词用驼峰命名法
#若类名后面有小括号,则为继承
color =
'green'
weight =
10
legs =
4
shell =
True
mouth =
'大嘴'
#方法实际就是函数,通过调用这些函数来完成某些工作
def
climb(self) :
print('爬')
def
run(self) :
print('')
def
bite(self) :
print('')
def
eat(self) :
print('')
以上代码定义了对象的特征(属性)和行为(方法),但还不是一个完整的对象,将定义的
这些称为类。需要使用类来创建一个真正的对象,这个对象叫作这个类的一个实例(实例对象)
创建一个对象,也叫类的实例化:
>>>#先运行p11_1.py
>>>tt
=Turtle()
>>>
注意:类后面跟的小括号,这跟调用函数是一样的,所以在python中,类名约定用大写字母
开头,函数用小写字母开头,更容易区分。另外赋值操作并不是必须的,但如果没有把创建好
的实例对象赋值给一个变量,那这个对象就没办法使用。最终被python的垃圾收集机制收回。
那如果要调用对象里的方法,使用点操作符(.)即可
>>>tt.climb() 爬
>>>tt.bite()
>>>tt.eat()
11.3 面向对象编程
答案:self可以理解为一个字典变量,内部存的就是对象的数据属性。如: {'name':'zhang','age':'18'}就是这些。
注意只有数据属性,并没有创建新的类的方法。 类----->通过实例化生成----对象---->
(对象只是一串类似于字典的数据)
面向对象编程
通过一个例子稍微感受一下:
>>> class Ball :
def
setName(self , name) :
self.name
= name
def
kick(self)
print("我叫%s" % self.name)
>>> a = Ball()
>>> a.setName("飞行流火")
>>> b= Ball()
>>> b.setName("团队之星")
>>> c=
Ball()
>>> c.setName("土豆")
>>>a.kick()
我叫飞行流火
>>>b.kick()
我叫团队之星
>>>c.kick()
我叫土豆
class.py
1 class Box:
2 def __init__(self, boxname, size, color):
3 self.boxname = boxname
4 self.size = size
5 self.color = color #self就是用于存储对象属性的集合,就算没有属性self也是必备的
6
7 def open(self, myself):
8 print('-->用自己的myself,打开那个%s,%s的%s' % (myself.color, myself.size, myself.boxname))
9 print('-->用类自己的self,打开那个%s,%s的%s' % (myself.color, myself.size, myself.boxname))
10
11 def close(self):
12 print('-->关闭%s,谢谢' % self.boxname)
13
14
15 b = Box('魔盒', '14m', '红色')
16 b.close()
17 b.open(b) #本来就会自动传一个self,现在传入b,就会让open多得到一个实例对象本身,print看看是什么。
18 print(b.__dict__) #这里返回的就是self本身,self存储属性,没有动作。
以上代码当中:
self 就是 对象/实例 属性的集合,
他们之间的关系如下:
1、Box是个类 ——》实例化——》b 对象/实例
2、Box类python中显示为: (接下一行)
——》实例化:b=Box('魔盒', '14m',
'红色') (接下一行)
——》得到对象/实例b,显示为<__main__.box object at>
0x00000233857AA518>
3、抽象体(接下一行)
——》实例的过程(接下一行)
——》得对对象,含有属性:{'boxname': '魔盒', 'size': '14m', 'color':
'红色'},即self。
self看似是一整个对象,实际上描述明白一个类就是生产对象的过程,描述了self就是得到了对象。所以self内的健值可以直接使用。
正如自然界中,一个有效的对象,必须包括:1、描述对象的属性;2、对象的方法。
所以self是必须的,也是对象中重要的特性。
当掌握了这个原理以后,我们试试以下的代码,感觉更加神奇。让我们明白重要的是它的思路:
class02.py
class Box:
def myInit(mySelf, boxname, size, color):
mySelf.boxname = boxname
mySelf.size = size
mySelf.color = color #自己写一个初始化函数,一样奏效,甚至不用self命名。其它函数当中用标准self
return mySelf #返回给实例化过程一个对象!神奇!并且含有对象属性/字典
#def __init__(self, boxname, size, color):
#self.boxname = boxname
#self.size = size
#self.color = color #注释掉原来标准的初始化
def open(self, myself):
print(self)
print('-->用自己的myself,打开那个%s,%s的%s' % (myself.color, myself.size, myself.boxname))
print('-->用类自己的self,打开那个%s,%s的%s' % (myself.color, myself.size, myself.boxname))
def close(self):
print('-->关闭%s,谢谢' % self.boxname)
#经过改造,运行结果和标准初始化没区别
b = Box().myInit('魔盒', '14m', '红色')
#b = Box('魔盒', '14m', '红色')#注释掉原来标准的初始化方法
b.close()
b.open(b) #本来就会自动传一个self,现在传入b,就会让open多得到一个实例对象本身,print看看是什么。
print(b.__dict__) #这里返回的就是self本身,self存储属性,没有动作。
换个角度来讲,
对类的操作有:1、定义属性 2、调节方法的选项
——》在适当的设置以后《———
类对的反馈有:1、得到属性 2、执行方法
在类的函数当中,self为什么是必要参数,因为他是对象的载体,可以理解成一个字典。
通过以下代码演示:
1 class Box:
2 def myInit(mySelf, boxname, size, color):
3 print(mySelf.__dict__)#显示为{}空字典
4 mySelf.boxname = boxname
5 mySelf.__dict__['aa'] = 'w'#甚至可以像字典一样操作
6 mySelf.size = size
7 mySelf.color = color #自己写一个初始化函数,一样奏效,甚至不用self命名。其它函数当中用标准self
8 return mySelf #返回给实例化过程一个对象!神奇!并且含有对象属性/字典
9
10 #def __init__(self, boxname, size, color):
11 #self.boxname = boxname
12 #self.size = size
13 #self.color = color #注释掉原来标准的初始化
14
15 def open(self, myself):
16 print(self)
17 print('-->用自己的myself,打开那个%s,%s的%s' % (myself.color, myself.size, myself.boxname))
18 print('-->用类自己的self,打开那个%s,%s的%s' % (myself.color, myself.size, myself.boxname))
19
20 def close(self):
21 print('-->关闭%s,谢谢' % self.boxname)
22
23
24 #经过改造,运行结果和标准初始化没区别
25
26 b = Box().myInit('魔盒', '14m', '红色')
27 #b = Box('魔盒', '14m', '红色')#注释掉原来标准的初始化方法
28 b.close()
29 b.open(b) #本来就会自动传一个self,现在传入b,就会让open多得到一个实例对象本身,print看看是什么。
30 print(b.__dict__) #这里返回的就是self本身,self存储属性,没有动作。
执行结果
{}
-->关闭魔盒,谢谢
<__main__.Box object at 0x0000026A2CBAA5F8>
-->用自己的myself,打开那个红色,14m的魔盒
-->用类自己的self,打开那个红色,14m的魔盒
{'boxname': '魔盒', 'aa': 'w', 'size': '14m', 'color': '红色'}
11.3.2 python的魔法方法
据说,Python
的对象天生拥有一些神奇的方法,它们总被双下划线所包围,他们是面向对象的
Python
的一切。他们是可以给你的类增加魔力的特殊方法,如果你的对象实现(重载)了这些
方法中的某一个,那么这个方法就会在特殊的情况下被 Python 所调用,你可以定义自己想要的
行为,而这一切都是自动发生的。
python的这些具有魔法的方法,总是被双下划线包围。此处讲其中一个最基本的特殊方法:__init
__(),关于python的其他魔法方法,详见第十二章。
通常把__init()__方法称为构造方法,__init()__方法的魔力体现在只要实例化一个对象,这个方法
就会在对象被创建时自动调用,其实,实例化对象是可以传入参数的,这些参数会自动传入__init__()
方法中,可以通过重写这个方法来自定义对象的初始化操作。举个例子:
>>>class Potato :
def __init__(self, name)
:
self.name
= name
def kick(self) :
print("我叫%s,噢 谁踢我?!" % self.name)
>>>p = Potato("土豆") #实例化对象传入参数土豆,自动传入__init__()方法中
>>>p.kick()
我叫土豆,噢 谁踢我?!
11.3.3 共有和私有
一般面向对象的编程语言都会区分共有和私有的数据类型,像C++和Java它们使用public和private
关键字,用于声明数据是共有的还是私有的,但在python中没有类似的关键字来修饰。
默认对象的属性和方法都是公开的,可以直接通过点操作符(.)进行访问:
>>>class Person :
name = "小甲鱼"
>>>p = Person()
>>>p.name
'小甲鱼'
为了实现类似私有变量的特征,python内部采用了一种叫 name mangling(名字改编)的技术,
在python中定义私有变量只需要在变量名或函数名前加上“__”两个下划线,那么这个函数或变量
就会成为私有的了:
>>> class Person :
__name = '小甲鱼'
>>> p = person()
>>> p.__name
报错
这样在外部将变量名“隐藏”起来了,理论上如果需要访问,就需要从内部进行:
>>>class Person :
def __init__(self, name)
self.__name =
name
def getName(self)
:
return
self.__name
>>> p = Person('小甲鱼')
>>> p.__name
报错
>>> p.getName()
'小甲鱼 '
>>>
p._Person__name #实际上在外部使用__类名__变量名
即可访问双下横线
’小甲鱼‘ #的私有变量了
注意:python目前的私有机制其实是为私有,python的类是没有控制权限的,所有变量
都可以被外部调用。
11.4 继承
语法很简单:
class 类名(被继承的类):
>>>class Parent :
def
hello(self):
print("正在调用父类的方法")
>>>class Child(Parent) :
pass
>>>p = Parent()
>>>p.hello()
正在调用父类的方法
>>>c = Child()
>>>c.hello()
正在调用父类的方法
注意:如果子类中定义与父类同名的方法或属性,则会自动覆盖父类对应的方法或属性:
>>>class Child(Parent) :
def
hello(self):
print("正在调用子类的方法")
>>>c = Child()
>>>c.hello()
正在调用子类的方法
尝试写一下刚才提到的金鱼(Goldfish)、鲤鱼(Carp)、三文鱼(Salmon),还有鲨鱼(Shark)
# p11_2.py
import random as r
class Fish:
def __init__(self):
self.x = r.random(0,10)
self.y = r.random(0,10)
def move(self):
self.x -= 1
print("我的位置是:",self.x ,
self.y)
class Goldfish(Fish):
pass
class Carp(Fish):
pass
class Salmon(Fish):
pass
#上面几个都是食物,不需要有个性,所以直接继承Fish类的全部属性和方法即可
#下边定义鲨鱼类,这是个吃货,除了继承Fish类的属性和方法,还要添加一个吃的方法
class Shark(Fish) :
def
__init__(self):
self.hungry
= True
def
eat(self):
if
self.hungry :
print("吃货的梦想天天有的吃")
self.hungry = False
else:
print("太撑了,吃不下")
>>>shark = Shark()
>>>shark.move()
程序报错 Shark属性没有x属性
原因其实是这样的:在Shark类中,重写了魔法方法__init__,但新的__init__方法里边没有初始化
鲨鱼的x坐标和y的坐标,因此调用move方法就会出错
11.4.1 调用未绑定的父类方法
class Shark() :
def __init__(self):
Fish.__init__(self)
self.hungry = True
在运行发现鲨鱼可以移动了:
>>>#先运行修改后的p11_2.py
>>>shark = Shark()
>>>shark.move()
我的位置是:7 9
>>>shark.move()
我的位置是: 6 9
11.4.2 使用super函数
#将p11_2.py鲨鱼的代码作如下修改
class
Shark(Fish):
def __init__(self):
super().__init__()
self.hungry = True
>>>先运行修改后的p11_2.py
>>> shark = Shark()
>>> shark.move()
我的位置是: 6 1
>>> shark.move()
我的位置是: 5 1
super函数的超级之处在于你不需要明确给出任何基类的名字,它会自动帮你找出所有基类以及
对应的方法
11.5 多重继承
class
类名(父类1,父类2,...)
11.6 组合
在Python里其实很简单,直接把需要的类放进去实例化就可以了,这就叫组合:
#p11_3.py
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)
self.fish = Fish(y)
def print_num(self):
print("水池里总共有乌龟%d只,小鱼%d条" %
(self.turtle.num , self.fish.num))
>>>pool = Pool(1,10)
>>>pool.print_num()
水池里总共有乌龟1只,小鱼10条
11.7 类、类对象和实例对象
>>>class C:
count = 0
>>> a = C()
>>> b = C()
>>> c = C()
>>> print(a.count, b.count,
c.count)
0 0 0
>>> c.count += 10
>>> print(a.count, b.count,
c.count)
0 0 10
>>>C.count += 100
>>>print(a.count, b.count,
c.count)
100 100 10
从上面例子可以看出,对实例对象c的count属性进行赋值后,就相当于覆盖了类对象C的count属性
如果没有赋值覆盖,那么引用的是类对象的count属性。
11.8 到底什么是绑定
>>>class BB :
def printBB() :
print("no zuo no die")
>>>BB.printBB()
no zuo no die
但这样做会有一个问题,根据类实例化后的对象根本无法调用里边的函数
>>>bb = BB()
>>>bb.printBB()
报错
实际上由于python的绑定机制,这里自动把bb对象作为第一个参数传入,所以才会出现typeerror
11.9 一些相关的BIF(内置函数)
1. issubcclass(class, classinfo)
如果第一个参数(class)是第二个参数(classinfo)的一个子类,则返回True,否则返回False
2. isinstance(object, classinfo)
如果第一个参数(object)是第二个参数(classinfo)的实例对象,则返回True,否则返回False
3. hasattr(object, name)
作用就是测试一个对象里是否有指定的属性
第一个参数是对象,第二个参数是属性名。
4. getattr (object, name[,default])
返回对象指定的属性值,如果指定的属性不存在,则返回default(可选参数的值);若没有设置
default参数,则抛出ArttributeError异常。
5. setattr(object, name, value)
与getattr对应,setattr()可以设置对象中指定属性的值,如果指定的属性不存在,则会新建属性并
赋值。
6. delattr(object, name)
与setattr()相反,delattr()用于删除对象中指定的属性,如果属性不存在则抛出错误
7. property(fget = None, fset = None, fdel =
None, doc = None)
property()是一个比较奇葩的BIF,它的作用是通过属性来设置属性。