python学习笔记6(面向对象基础)

定义

面向过程:1—>2—>3—>4 怎么做
面向对象:4 谁来做
相对于函数来说是一个更大的封装,根据职责将对象所能实现的功能封装到一起
1.确定对象要做的事情(方法->多个)
2.让对象调用方法
面向对象的语言:java c++ python

类和对象

  • 类:是一群具有相同特征和行为事物的一个统称(抽象的–>不能直接使用)
    特征:被称为属性
    行为:被称为方法

  • 对象:由类创建出来的一个具体的存在,由哪一个类创建出来的
    对象,就拥有哪一个类中定义的方法和属性

  • 先有类–>再有对象

  • 类和对象的关系:
    类是模板,对象是根据这个模板创建出来的,先有类再有对象
    类只需要有一个,对象可以有很多个
    不同对象(由同一类创建出来的对象)之间的属性可以不相同
    类中定义了什么属性和方法,使用这个类创建出来的对象就有什么
    属性和方法,不可能多也不可能少

  • 类的设计
    1.类名:这类事物的名字(大驼峰命名法)
    大驼峰命名法
    1.每一个单词的首字母大写
    2.单词与单词之间没有下划线
    2.属性:这个类创建出来的对象有什么样的特征
    3.方法:这个类创建出来的对象有什么样的行为

  • 类名的确定
    名词提炼法分析整个业务流程,出现的名词,通常就是找到的类

  • 属性和方法的确定
    对 对象的特征描述,通常可以定义成属性
    对象具有的行为(动词),通常可以定义为方法
    提示:需求中没有涉及的属性或方法在设计类时,不需要考虑

练习

练习1:
需求:
小明今年18岁,身高1.75,每天早上要跑步,小明吃东西
小美今年17岁,身高1.65 小美不跑步,小美吃东西

类名:Person
属性:name,age,height
方法:run,eat

练习2:
一只黄颜色的狗狗叫大黄
看见生人旺旺叫
看见家人摇尾巴

类名:Dog
属性:color,name
方法:bark,shake

练习2

例:猫爱吃鱼,猫要喝水
哪一个对象调用的方法,self就是哪一个对象的引用
在类封装方法的内部,self就表示当前调用方法的对象自己
在的方法内部 可以通过 self.属性 来访问对象的属性

class Cat:
    def eat(self):
        print('%s 爱吃鱼' %(self.name))

    def drink(self):
        print('猫要喝水')

创建猫对象

tom = Cat()

在类的外部 对象.属性 可以给对象添加属性(不推荐使用)

对象包含哪些方法,应该封装再类中

tom.name = 'Tom'
print(tom)

在这里插入图片描述

addr = id(tom)
print(addr)
# %x:打印格式为十六进制
print('%x' %(addr))
# %d:打印格式为十进制
print('%d' %(addr))
tom.eat()
tom.drink()

在这里插入图片描述

# 再创建一个猫对象
lazy_cat = Cat()
lazy_cat.name = 'miaomiao'
print(lazy_cat)
lazy_cat.eat()
lazy_cat.drink()

lazy_cat2 =lazy_cat
print(lazy_cat2)

在这里插入图片描述

初始化方法

  • 初始化方法
    我们现在已经知道了使用 类名() 就可以创建一个对象
    当使用类名()创建对象时,python的解释器会自动执行以下操作:
    1.为对象在内存中分配空间–创建对象
    2.调用初始化方法为对象的属性设置初始值–初始化方法(init)
    这个初始化方法就是__init__方法,__init__是对象的内置方法
    __init__方法是专门用来定义一个类具有哪些属性的方法
  • 改造初始化方法–初始化的同时设置初始值:
    在开发中,如果希望在创建对象的同时,就设置对象的属性,可以对__init__方法进行改造
    1.把希望设置的属性值,定义成__init__方法的参数
    2.在方法内部使用self.属性名 = 形参 接收外部传递的参数
    3.在创建对象时,使用类名(属性1,属性2…)调用
class Cat:
    def __init__(self,new_name):
        print('这是一个初始化方法')
        # self.属性名 = 属性的初始值
        #self.name = 'Tom'
        self.name = new_name
    # 在类中,任何方法都可以使用self.name
    def eat(self):
        print('%s 爱吃鱼' %(self.name))

# 使用类名()创建对象的时候,会自动调用初始化方法__init__
tom = Cat('tom')
# 定义属性之后,再用Cat创建对象的时候,都会拥有该属性
print(tom.name)
tom.eat()

lazy_cat = Cat('miaomiao')
print(lazy_cat.name)
lazy_cat.eat()

在这里插入图片描述

对象的销毁

__del__:对象被从内存中销毁前,会被自动调用
  • 在python中
    当使用类名()创建对象时,为对象分配完空间后,自动调用__init__方法
    当一个对象被从内存中销毁前(把这个对象从内存中删除掉),
    会自动调用__del__方法
应用场景
__init__改造初始化方法,可以让创建对象更加灵活
__del__如果希望在对象被销毁前,再做一些事情,可以考虑一下__del__方法

生命周期(出生到死亡)
一个对象从调用类名()创建,生命周期开始
一个对象的__del__方法一但被调用,生命周期结束
在对象的生命周期内,可以访问对象属性,或者让对象调用方法
class Cat:
    def __init__(self,new_name):
        self.name = new_name
        print('%s 来了' %(self.name))
    def __del__(self):
        print('%s 走了' %(self.name))

cat = Cat('tom')
print(cat.name)
# del关键字可以删除一个对象,del关键字自己调用__del__方法
del cat
print('*' * 50)

在这里插入图片描述

返回对象描述信息

__str__:返回对象的描述信息 print函数输出使用
  • 在python中,使用python输出对象变量,默认情况下,
    会输出这个变量引用的对象是由哪>一个类创建的对象,
    以及在内存中的地址(十六进制表示)
    如果在开发中,希望使用print输出对象变量时,
    能够打印自定义的内容,就可以利用__str__这个内置方法了
class Cat:
    def __init__(self,new_name):
        self.name = new_name
    def __str__(self):
        # 必须返回一个字符串
        return '我是 %s' %(self.name)

cat = Cat('tom')
print(cat)

在这里插入图片描述

创建栈的类

"""
栈的方法
    入栈 出栈 取栈顶元素
    栈的长度 判断栈是否为空
    显示栈的元素
"""
class Stack:
    def __init__(self):
        self.stack=[]

    def push(self,value):
        self.stack.append(value)
        return True
    def pop(self):
        if self.stack:
            # 获取出栈元素,并返回
            item = self.stack.pop()
            return item
        else:
            return False

    def top(self):
        if self.stack:
            return self.stack[-1]
        else:
            return False

    def length(self):
        return len(self.stack)

    def isempty(self):
        return self.stack == []

    def view(self):
        return ','.join(self.stack)

s = Stack()
s.push('1')
s.push('2')
print(s.view())
item = s.pop()
print(item)
print(s.view())

在这里插入图片描述

封装(面向对象三大特性之一)

封装
1.封装是面向对象编程的一大特点
2.面向对象编程的第一步 将属性和方法封装到一个抽象的类中(为什么说是抽象的,因为类不能直接使用)
3.外界使用类创建对象,然后让对象调用方法
4.对象方法的细节都被封装在类的内部

练习

  • 需求1:
    1.小明和小美都爱跑步
    2.小美体重45.0公斤
    3.小明体重75.0公斤
    4.每次跑步都会减少0.5公斤
    5.每次吃东西都会增加1公斤
class Person:
    def __init__(self,name,weight):
        self.name = name
        self.weight = weight


    def __str__(self):
        return ('我的名字叫 %s 体重是 %.2f' %(self.name,self.weight))
# 在对象方法内部,可以直接访问对象的属性
    def run(self):
        print('%s 爱跑步' %(self.name))
        self.weight -= 0.5

    def eat(self):
        print('%s 吃东西' %(self.name))
        self.weight += 1


xiaoming = Person('小明',75.0)
xiaoming.run()
xiaoming.eat()
#print(xiaoming.weight)
print(xiaoming)


# 同一个类创建出多个对象之间,属性互不干扰的
xiaomei = Person('小美',45.0)
xiaomei.run()
xiaomei.eat()
print(xiaomei)
print(xiaoming)

在这里插入图片描述

  • 需求:
    1.房子有户型,总面积和家具名称列表
    新房子没有任何的家具
    2.家具有名字和战地面积,其中
    床:占4平米
    衣柜:占2平米
    餐桌:占1.5平米
    3.将以上三件家具添加到房子中
    4.打印房子时,要求输出:户型,总面积,剩余面积,家具名称列表
    两个类
    家具类
    房子类
    因为房子类要使用家具 被使用的类 应该先开发
class HouseItem:
    # 初始化方法
    def __init__(self, name, area):
        self.name = name
        self.area = area

    def __str__(self):
        return '[%s] 占地 %.2f' % (self.name, self.area)


class House:
    def __init__(self,house_type,area):
        self.house_type = house_type
        self.area = area
        # 剩余面积
        self.free_area = area
        # 家具名称列表
        self.item_list = []

    def __str__(self):
        return ('户型:%s\n总面积:%.2f[剩余:%.2f]\n家具:'
                '%s' %(self.house_type,self.area,self.free_area,self.item_list))

    def add_item(self,item):
        #print('要添加 %s' %(item))
        #1.判断家具的面积
        if item.area > self.free_area:
            print('%s的面积太大了,无法添加' %(item.name))
            return
        #2.将家具的名称添加到列表中
        self.item_list.append(item.name)
        #3.计算剩余面积
        self.free_area -= item.area

# 1.创建家具
bed = HouseItem('bed', 4)
print(bed)
chest = HouseItem('chest', 2)
print(chest)
table = HouseItem('table', 1.5)
print(table)


# 2.创建房子对象
my_home = House('两室一厅',100)
#print(my_home)
# 将家具添加到房子里
my_home.add_item(bed)
my_home.add_item(chest)
my_home.add_item(table)
print(my_home)


"""
小结:
	1.主程序只负责创建房子对象和家具对象
	2.让 房子 对象调用add_item 方法 将家具添加到房子中
	3.面积计算,剩余面积,家具列表 等处理都被封装到房子类的内部


    1.封装是面向对象编程的一大特点
	2.面向对象编程的第一步 -- 将 属性 和 方法 封装到一个抽象的类中
	3.外界使用 类 创建 对象,然后让对象调用方法
	4.对象方法的细节都被封装在类的内部
"""

在这里插入图片描述

  • 演练重点:一个对象的属性可以是另外一个类创建的对象
    示例:士兵射击
    需求:
    1.士兵瑞恩有一把AK47
    2.士兵可以开火(士兵开火扣动的是扳机)
    3.枪 能够 发射子弹(把子弹发射出去)
    4.枪 能够 装填子弹 --增加子弹的数量
class Gun:
    def __init__(self,model):
        # 枪的型号
        self.model = model
        # 子弹的数量(调用增加子弹的方法来增加子弹数量)
        self.bullet_count = 0

    def add_bullet(self,count):
        self.bullet_count += count

    def shoot(self):
        # 判断子弹数量
        if self.bullet_count <= 0:
            print('[%s] 没有子弹了...' %(self.model))
            return
        # 发射子弹 子弹数量变化
        self.bullet_count -= 1
        print('[%s] tututu~ [%d]' %(self.model,self.bullet_count))

class Soldier:
    def __init__(self,name):
        self.name = name
        # 枪(新兵没有枪)
        self.gun = None

    def fire(self):
        # 1.判断士兵是否有枪
        if self.gun == None:
            print('[%s] 还没有枪..' %(self.name))
            return
        print('go!!![%s]' %(self.name))
        self.gun.add_bullet(50)
        self.gun.shoot()


# 创建枪对象
ak = Gun('ak47')
# ak.add_bullet(50)
# ak.shoot()
# ak.shoot()

# 创建士兵对象
ryan = Soldier('Ryan')
# 通过主程序的赋值语句,士兵有枪了
ryan.gun = ak
ryan.fire()
#print(ryan.gun)

在这里插入图片描述

继承(面向对象三大特性之一)

  • 继承:实现代码的重用,相同的代码不需要重复的写
  • 单继承:
    子类拥有父类的所有方法和属性(子类只需封装自己特有的方法)
class Animal:
    def eat(self):
        print('吃')
    def drink(self):
        print('喝')
    def run(self):
        print('跑')
    def sleep(self):
        print('睡')

class Cat(Animal):
    def call(self):
        print('喵喵~')

class Hellokitty(Cat):
    def speak(self):
        print('我可以说日语~')

# 创建猫对象
fentiao = Cat()
fentiao.eat()
fentiao.drink()
fentiao.run()
fentiao.sleep()
fentiao.call()

hk = Hellokitty()
hk.speak()
# 子类可以继承父类的所有属性和方法
hk.call()

# 继承的传递性,子类拥有父类的父类的属性和方法
hk.eat()
hk.drink()
hk.run()
hk.sleep()

"""
方便同学们在网上查阅一些相关的文章的时候不会懵
专业术语:
Cat类是Animal类的子类,Animal类是Cat类的父类,Cat从Animal类继承
Cat类是Animal类的派生类,Animal类是Cat类的基类,Cat类从Animal类派生
"""

在这里插入图片描述
在这里插入图片描述

  • 当父类的方法不能满足子类的需求的时候,可以对父类的方法进行重写
    1.覆盖父类方法
    2.对父类方法进行扩展
class Animal:
    def eat(self):
        print('吃')
    def drink(self):
        print('喝')
    def run(self):
        print('跑')
    def sleep(self):
        print('睡')

class Cat(Animal):
    def call(self):
        print('喵喵~')

class Hellokitty(Cat):
    def speak(self):
        print('我可以说日语~')
    def call(self):
        # 调用原本在父类中封装的方法
        #Cat.call(self)
        super().call() # 只在python3中支持
        print('#@!#@!#@!')

hk = Hellokitty()
hk.speak()
hk.call()

在这里插入图片描述

  • 多继承
    子类拥有一个父类叫做单继承
    子类可以拥有多个父类,并且具有所有父类的属性和方法
    例如:孩子会继承自己父亲和母亲的特性
  • 多继承的注意事项:
    问题的提出:
    如果不同的父类中存在同名的方法,子类对象在调用方法时,会调用哪一个父类中的方法呢?
    提示:开发时,应尽量避免这种容易产生混淆的情况
    如果父类之间存在同名的属性或方法,因该尽量避免使用多继承
class A:
    def test(self):
        print('A----test 方法')
    def demo(self):
        print('A----demo 方法')

class B:
    def test(self):
        print('B----demo 方法')
    def demo(self):
        print('B----test 方法')


class C(B,A):						#(B在第一位优先继承B)
    pass

c = C()
c.test()
c.demo()

在这里插入图片描述

新式类和旧式(经典)类:
object是Python为所有对象提供的基类,提供有一些内置的属性和方法,可以使用dir函数查看
新式类:以object为基类的类,推荐使用
经典类:不以object为基类的类,不推荐使用
在python3.X中定义的类时,如果没有指定父类,会默认使用object作为基类--python3.x中定义的类都是新式类
在python2.x中定义类时,如果没有指定父类,则不会以object作为基类
####推荐使用新式类#############

新式类和旧式类在多继承时---会影响到方法的搜索顺序

为保证编写的代码能够同时在python2.x和python3.x运行
今后在定义类时,如果没有父类,建议统一继承自object
# 当多个子类都调用父类的属性时,父类被重复调用多次
# 显然这会影响代码的执行效率
class Parent(object):
    def __init__(self,name):
        print('parent的init开始被调用')
        self.name = name
        print('parent的init结束被调用')

class Son1(Parent):
    def __init__(self,name,age):
        print('Son1的init开始被调用')
        self.age = age
        Parent.__init__(self,name)
        print('Son1的init结束被调用')

class Son2(Parent):
    def __init__(self,name,gender):
        print('Son2的init开始被调用')
        self.gender = gender
        Parent.__init__(self,name)
        print('Son2的init结束被调用')

class Grandson(Son1,Son2):
    def __init__(self,name,age,gender):
        print('grandson的init开始被调用')
        # 调用Son1类的年龄属性
        Son1.__init__(self,name,age)
        # 调用Son2类的性别属性
        # Son1和Son2都调用Parent类的姓名属性
        Son2.__init__(self,name,gender)
        print('grandson的init结束被调用')


gs = Grandson('grandson',18,'男')
print('姓名:',gs.name)
print('年龄:',gs.age)
print('性别:',gs.gender)

在这里插入图片描述

新式类:调用的顺序为广度优先
旧式类:调用顺顺序为深度优先
super()类:不重复的调用父类,解决了钻石继承(多继承)的难题
class Person(object):
    def __init__(self):
        print('我要好好学习')
    def study(self):
        print('我要好好学习语言')

class Man(Person):
    def __init__(self):
        print('我是男的我要好好学习')
    def study(self):
        print('我要学好数学')
        #super().study()
class Womam(Person):
    def __init__(self):
        print('我是女的我要好好学习')
    def study(self):
        print('我要学好英语')
        super().study()

class Son(Man,Womam):
    def __init__(self):
        print('我是儿子我要好好学习')

    def study(self):
        print('我要学好化学和物理')
        #Womam.study(self)
        #Man.study(self)
        super().study()
print(Son.mro())
son1 = Son()
son1.study()

在这里插入图片描述

super实现原理:通过c3算法,生成mro(method resolution order)列表,根据列表中元素顺序查询调用
super()不是指父类 而是指以实例化对象为起点的mro序列中的下一个
  
简洁点的三个原则就是:
子类在父类前,所有类不重复调用,从左到右
# 试分析下列类的mro序列
class A():
    def go(self):
        print ("go A go!")
    def stop(self):
        print ("stop A stop!")
    def pause(self):
        raise Exception("Not Implemented")
class B(A):
    def go(self):
        super(B, self).go()
        print ("go B go!")
class C(A):
    def go(self):
        super(C, self).go()
        print ("go C go!")
    def stop(self):
        super(C, self).stop()
        print ("stop C stop!")
class D(B,C):
    def go(self):
        super(D, self).go()
        print ("go D go!")
    def stop(self):
        super(D, self).stop()
        print ("stop D stop!")
    def pause(self):
        print ("wait D wait!")
class E(B,C):
    pass
# A
print(A.mro())
# B-->A
print(B.mro())
# C-->A
print(C.mro())
# D-->B-->C-->A
print(D.mro())
# E-->B-->C-->A
print(E.mro())

在这里插入图片描述

多态(面向对象三大特性之一)

  • 在继承关系中,如果一个实例的数据类型是某一个子类,则其数据类型也可看作是该子类的父类。反之,父类不可当作子类。
# 定义一个父类Animal
class Animal(object):
    pass
# 定义一个子类Tiger
class Tiger(Animal):
    pass
# 定义另一个子类Cat
class Cat(Animal):
    pass
a=Animal()
b=Tiger()
c=Cat()
print(isinstance(a,Animal))
# 由父类创建的对象,其数据类型不能为子类实例化对象的数据类型
print(isinstance(a,Tiger))
# 由子类创建的对象,其数据类型可以当作是父类实例化对象的数据类型
print(isinstance(b,Animal))
print(isinstance(b,Tiger))
print(isinstance(c,Cat))

在这里插入图片描述

私有属性和私有方法

私有属性和私有方法
应用场景及定义方式
应用场景
	在实际开发中,对象的某些属性或方法可能只希望在对象的内部使用,而不希望在外部被访问到
	私有属性 就是 对象 不希望公开的 属性
	私有方法 就是 方法 不希望公开的 方法
定义方法
	在定义属性或方法时,在属性名或者方法名前增加两个下划线,定义的就是私有属性或方法
class Women(object):
    def __init__(self,name):
        self.name = name
        self.__age = 18

    def __secret(self):
        print('%s 的年龄是 %d' %(self.name,self.__age))

lily = Women('lily')
#print(lily.age)
lily.__secret()

在这里插入图片描述

类属性和类方法

  • 一切皆对象
    类也是一个特殊的对象–>类对象
    类属性
    类方法
  • 需求:定义一个工具类
    每件工具都有自己的名字
    需要知道使用这个类,创建了多少个工具对象
class Tool(object):
    # 1.使用赋值语句定义类属性,记录所有工具的数量
    count = 0

    def __init__(self,name):
        self.name = name
        # 让类属性的值 +1
        Tool.count += 1
    @classmethod
    def show_tool_count(cls):
        # cls.count 在类方法内部,访问当前的类属性
        print('工具对象的数量 %d' %(cls.count))

# 创建工具对象(对象在创建的时候,会自动调用初始化方法)
print(Tool.count)
tool1 = Tool('斧头')
tool2 = Tool('榔头')
tool1.show_tool_count()
# 输出工具对象的属性
# 使用 类名.属性名 的方式来获取属性名
print(Tool.count)

在这里插入图片描述

类是一个特殊的对象
Python中一切皆对象
	class AAA: 定义的类属性属于类对象
	obj1 =AAA: 属于实例对象
在运行程序时,类 同样会被加载到内存
在python中,类 是一个特殊的对象--类对象
在程序运行时,类对象(模板) 在内存中之有一份,使用一个类(模板)可以创建出很多个对象实例
除了封装 实例 的属性和方法外,类对象还可以有自己的属性和方法
1.类属性
2.类方法
通过 类名. 的方式可以直接访问类的属性或者调用类的方法


类方法需要用修饰器@classmethod来标示,告诉解释器这是一个类方法
类方法的第一个参数应该是cls
	由哪一个类调用的方法,方法内的cls就是哪一个类的引用
	这个参数和实例方法的第一个参数是self类似(哪一个对象调用的方法,self就是哪一个对象的引用)

通过类名调用类方法,调用方法时,不需要传递cls参数

在方法内部
	也可以通过cls.访问类的属性
	也可以通过cls.调用其他的类方法

静态方法

class Cat(object):
    @staticmethod
    def call():
        print('喵~')

# 不需要创建对象,直接就可以使用
Cat.call()

在这里插入图片描述

class Date(object):
    def __init__(self,year,month,day):
        self.year = year
        self.month = month
        self.day = day
    # 实例方法(默认情况下会传递对象给echo方法)
    def echo(self):
        print('%s %s %s' %(self.year,self.month,self.day))

    # 默认传递类本身给这个方法
    @classmethod
    def as_string(cls,s):
        #print(cls)
        month,day,year = s.split('/')
        d = cls(year,month,day) # d = Date(2018,10,10)
        return d

    @staticmethod
    def is_vaild(s):
        # 批量将年月日转换成整型
        # month,day,year = s.split('/')
        # month,day,year = [int(i) for i in s.split('/')]
        month,day,year = map(int,s.split('/'))
        return 0<month<=12 and 0<day<=31 and 1<year< 9999

d = Date(2018,10,10)
print(d)
d.echo()

s = '10/10/2018'
print(Date.as_string(s))
print(Date.is_vaild(s))

# d = Date(2018,10,10)
# print(d.is_vaild('13/10/2019'))

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/mkgdjing/article/details/85337606