python学习笔记19(面向对象编程)

Source

面向对象编程

基础概念:
对象: 把数据及对数据的操作方法放在一起,作为一个相互依存的整体,即为对象。
类: 对同类对象抽象出其共性,形成类。
注意:
类中的大多数数据,只能用本类的方法进行处理。
类通过一个简单的外部接口与外界发生关系,对象与对象之间通过消息进行通信

面向对象是相对面向过程而言,面向对象和面向过程都是一种思想

面向过程强调的是功能行为,关注的是解决问题需要哪些步骤

面向对象将功能封装进对象,强调具备了功能的对象,关注的是解决问题需要哪些对象

python中的类:
Python中用类可以和现实中描述物的方法对应,一般描述一种事物都会涉及到这种事物的属性以及功能
属性——对应类中的成员变量。
功能——对应类中的成员方法。

定义类其实在定义类中的成员(成员变量和成员方法), 拥有相同(或者类似)属性和行为的对象都可以抽像出一个类

创建类
类作为一种数据类型,本身并不占内存空间,和所学过的number,string,boolean等类似。用类创建实例化对象(变量),对象占内存空间

创建格式:

class  类名(父类列表):
    属性
    行为

示例:

class Person(object):
#object:基类,超类,所有类的父类,一般没有合适的父类就写object

    #定义属性(定义变量)
    name = ""
    age = 0
    height = 0
    weight = 0

    #定义方法(定义函数)
    #方法的参数必须以self当第一个参数
    #self代表类的实例(某个对象)
    def run(self):   #不用传参数也要有self,self可以改为其他名字
        print("run")
    def eat(self, food): #要传递参数第一个必须写self,self可以改为其他名字
        print("eat" + food)

实例化对象: 创建一个符合这个类中的对象
格式: 对象名 = 类名(参数列表)
注意: 没有参数,小括号也不能省略
示例:

#实例化一个对象
per1 = Person()
print(per1)
print(type(per1))
print(id(per1))
#输出:
<__main__.Person object at 0x000002577C56DFD0>
<class '__main__.Person'>
2574771478480

per2 = Person()
print(per2)
print(type(per2))
print(id(per2))
#输出:
<__main__.Person object at 0x000002577C3B6860>
<class '__main__.Person'>
2574769678432     #和前面的对象per1地址不同

访问对象中的属性和方法
访问属性
格式: 对象名.属性名
赋值: 对象名.属性名 = 新值
访问方法
格式: 对象名.方法名(参数列表)

#访问属性
per = Person()
per.name = "tom"
per.age = 18
per.height = 160
per.weight = 80
print(per.name, per.age, per.height, per.weight)
#输出:tom 18 160 80

#访问方法
per.run()
per.eat("apple")
# 输出:
run
eat apple

如何使利用Person创建的对象的默认属性不一样? 不需要创建后再重新赋值
构造函数: _init_() 在使用类创建对象的时候自动调用
注意: 如果不显式的写出构造函数,默认也会自动添加一个空的构造函数
示例:

class Person(object):
    #name = "stu"
    #age = 10
    #height = 160
    #weight = 90
    def run(self):
        print("run")
    def eat(self, food):
        print("eat " + food)

    def __init__(self, name, age, height, weight):
        #定义属性,使用类创建对象时,自动调用,对对象的属性赋值
        self.name = name
        self.age = age
        self.height = height
        self.weight = weight
#创建对象
per = Person("hanmeimei", 20, 170, 55)
print(per.name, per.age)
#输出:hanmeimei 20

对self的理解:
self代表类的实例,而非类。哪个对象调用方法,那么该方法中的self就代表那个对象
self._class_ 代表类名
示例:

class Person(object):
    def run(self):
        print("run")
        print(self.__class__)    #打印类名
        p = self.__class__("tt", 30, 10, 30)    #创建了一个对象p,只存在与run中
        print(p)
        print(p.name)   
    def eat(self, food):
        print("eat " + food)
    def say(self):
        print("Hello! my name is %s, I am %d years old" % (self.name, self.age))
    #self不是关键字,换成其他的标识符也是可以的,但是帅的人都是用self
    def play(a):
        print("play " + a.name)
    def __init__(self, name, age, height, weight):
        self.name = name
        self.age = age
        self.height = height
        self.weight = weight

per1 = Person("tom", 20, 160, 80)
per1.say()
#输出: Hello! my name is tom, I am 20 years old
per1.play()
#输出:play tom
per1.run()
#输出:
run
<class '__main__.Person'>
<__main__.Person object at 0x000002C5318A7BE0>
tt

析构函数:_del_()
在释放(删除)对象时自动调用
示例:

class Person(object):
    def run(self):
        print("run")
    def eat(self, food):
        print("eat " + food)
    def __init__(self, name, age, height, weight):
        self.name = name
        self.age = age
        self.height = height
        self.weight = weight
    def __del__(self):
        print("这里是析构函数")

per = Person("hanmeimei", 20, 170, 55)
#释放对象
del per
#输出:这里是析构函数

#对象释放以后就不能再访问了
print(per.age)    #报错

#在函数里定义的对象,会在函数结束时自动释放,这样可以用来减少内存空间的浪费
def func():
    per2 = Person("aa", 1, 1, 1)

func()
#输出:这里是析构函数

#即使没有执行del语句,在创建完对象后,有时析构函数也会执行,用pass取消执行
while 1:
    pass

其他默认函数:

_str_(): 在调用print打印对象时自动调用,是给用户用的,是一个描述对象的方法。

_repr_(): 是给机器用的,在Python解释器里面直接敲对象名在回车后调用的方法

注意: 在没有str但有repr时,str = repr

优点: 当一个对象的属性值很多,并且都需要打印,运用__str__方法可简化代码

示例:

class Person(object):
    def __init__(self, name, age, height, weight):
        self.name = name
        self.age = age
        self.height = height
        self.weight = weight
    def __str__(self):
        return "%s-%d-%d-%d" % (self.name, self.age, self.height, self.weight)
 
per = Person("hanmeimei", 20, 170, 55)
#print(per.name, per.age, per.height, per.weight)
print(per)
#输出:hanmeimei-20-170-55

设置对象属性的访问权限:
如果要让对象的内部属性不被外部直接访问,在属性前加两个下划线(__),在Python中如果在属性前加两个下划线,那么这个属性就变成了私有属性

示例:

class Person(object):
    def money(self):
        print(self.__money)
    def eat(self, food):
        print("eat " + food)
    def setMoney(self, money):
        #数据的过滤
        if money < 0:
            money = 0
        self.__money = money
    def getMoney(self):
        return self.__money
    def __init__(self, name, age, height, weight, money):
        self.name = name
        self.age = age
        self.height = height
        self.weight = weight
        self.__money = money#_Person__money    #设置成私有属性      

通过内部的方法,去修改私有属性
通过自定义的方法实现对私有属性的赋值与取值

per = Person("hanmeimei", 20, 170, 55, 100000)
print(per.__money)  #外部使用
#报错

per.money()   #内部可以使用
#输出:100000
per.setMoney(10)
print(per.getMoney())
#输出:10

原理 : 不能直接访问per.__money是因为Python解释器把__money变成了_Person__money,仍然可以用_Person__money去访问,但是强烈建议不要这么干,不同的解释器可能存在解释的变量名不一致

per._Person__money = 1
print(per.getMoney())
#输出:1

在Python中 __XXX 变量,这样的实例变量外部是可以访问的,但是,按照约定的规则,当我们看到这样的变量时,意思是“虽然我可以被访问,但是请把我视为私有变量,不要直接访问我”

综合示例:模拟人开枪射击

'''
人
类名:Person
属性:gun
行为:fire

枪
类名:Gun
属性:bulletBox
行为:shoot

弹夹
类名:BulletBox
属性:bulletCount
行为:
'''
class BulletBox(object):
    def __init__(self, count):
        self.bulletCount = count

class Gun(object):
    def __init__(self, bulletBox):
        self.bulletBox = bulletBox
    def shoot(self):
        if self.bulletBox.bulletCount == 0:
            print("没有子弹了")
        else:
            self.bulletBox.bulletCount -= 1
            print("剩余子弹:%d发" % (self.bulletBox.bulletCount))

class Person(object):
    def __init__(self, gun):
        self.gun = gun
    def fire(self):
        self.gun.shoot()
    def fillBullet(self, count):
        self.gun.bulletBox.bulletCount = count

#弹夹
bulletBox = BulletBox(5)

#枪
gun = Gun(bulletBox)

#人
per = Person(gun)

per.fire()
per.fire()
per.fire()
per.fire()
per.fire()

per.fire()
per.fire()

per.fillBullet(2)
per.fire()
per.fire()
per.fire()