二、Python面向对象(一)

面向对象

面向对象

  • 对象是对变量和函数的封装
  • 基本代码还是封装在函数中
  • 通过对象, 把函数再次封装, 可以减少函数的数量
  • 即使代码很多 函数很多,可以把不同的函数放入不同的对象, 这样有效降低了对象的数量
  • 类似c++,java包括python都属于面向对象的语言

类与对象

    • 类是一个模板, 不能直接使用
    • 类似于是一张设计图纸
  • 对象
    • 是通过类创建出来的
    • 对象可以使用
  • 方法
    • 把函数封装到对象内部, 叫对象的方法(类似于函数)
  • 属性
    • 把变量封装到对象内部, 叫对象的属性(类似于变量)

方法与属性

  • 方法是函数演化来的, 方法是用来执行的, 代表一种行为
  • 属性是变量演化来的, 属性只是一个具体的值

一个案例

一个小狗叫拜登, 毛是白色的, 小狗可以汪汪叫, 可以撒泼打滚
小狗的名字叫拜登
小狗的颜色是白色
小狗的行为汪汪叫
小狗的行为撒泼打滚
对象是小狗
属性, 名字name, 值为 "拜登"
属性, 颜色color, 值为 "白色"
方法 汪汪叫  wangwang
方法 撒泼打滚  dagun

类的设计

  • 设计一个类的三个要素

    • 类名
    • 方法
    • 属性
  • 设计一个人(person)类

    • 属性
      • 姓名name
      • 年龄age
      • 身高height
    • 方法
      • 跑run
      • 吃eat
  • person类可以设计多个对象

  • 属性

    • 姓名name-小明
    • 年龄age-18
    • 身高height-1.75
  • 方法

    • 跑run, 喜欢早上跑步
    • 吃eat
  • person类可以设计多个对象

  • 属性

    • 姓名name-小美
    • 年龄age-17
    • 身高height-1.65
  • 方法

    • 跑run
    • 吃eat

类与对象的关系

  • 先有类, 然后才有对象
    • 一定先有图纸, 然后才能按图纸盖房子
  • 一个类可以创建多个对象
    • 一个图纸, 可以盖多套房子
  • 类中有什么属性和方法, 对象中就有什么属性和方法
    • 图纸是什么样, 房子盖好了, 就什么样
  • 不同的对象, 属性的值可能不一样
    • 按图纸造飞机, 编号是属性, 但每架飞机的编号可能是不同的
    • 按图纸造飞机, 颜色是属性, 但每架飞机的颜色可能是不同的

类的定义

class 类名: # 类名习惯用大驼峰
    # 定义一个方法
    def 方法名(self, 形参1, 形参2......):
        pass
# 方法一定要定义在类的内部, 和类名有一个tab缩进
# 方法的第一个形参一定为self, 其他的语法与函数一致
# 定义一个类, 名字叫Dog
class Dog:
    def wangwang(self): # 定义了一个方法
        print("汪汪叫")

    def dagun(self): # 定义了一个方法
        print("满地打滚")

# 定义类和定义函数道理一样, 代码不会执行

对象的创建

对象名 = 类名() # 根据类创建一个对象
对象名.方法名()  # 调用对象的方法
# 定义一个类, 名字叫Dog
class Dog:
    def wangwang(self): # 定义了一个方法
        print("汪汪叫")

    def dagun(self): # 定义了一个方法
        print("满地打滚")

# 定义类和定义函数道理一样, 代码不会执行

d = Dog() # 根据类Dog创建一个对象d
d.wangwang()  # 调用对象d的wangwang方法
d.dagun()  # 调用对象d的dagun方法

术语

  • 实例
    • 一个对象就是一个类的实例
    • 对象d就是类Dog的一个实例
  • 实例化
    • 创建对象的过程就是实例化 d = Dog()这条语句就是d的实例化过程
d1 = Dog()  # d1是Dog的实例, 这条语句就是实例化的体现

定义一个cat类

class Cat: # 定义一个Cat类
    def eat(self): # 定义一个方法eat
        print("吃")

    def drink(self):  # 定义一个方法drink
        print("喝")

c1 = Cat() # 把Cat类实例化为对象c1, c1位Cat类的实例
c1.eat()  # 调用对象c1的eat方法
c1.drink()  # 调用对象c1的drink方法

c2 = Cat() # 把Cat类实例化为对象c2, c2位Cat类的实例
c2.eat()  # 调用对象c2的eat方法
c2.drink()  # 调用对象c2的drink方法

self的作用

self的第一个作用-在方法中定义属性

# 在方法内部
self.属性名 = 值
# Cat有eat和drink两个方法
# Cat有name和age两个属性
# Cat添加一个方法, set, 方法的作用是定义name和age两个属性
class Cat: # 定义一个Cat类
    def eat(self): # 定义一个方法eat
        print("吃")

    def drink(self):  # 定义一个方法drink
        print("喝")

    def set(self):
        self.name = "小猫" # 给Cat类定义了一个属性name, 值为"小猫"
        self.age = 1 # 给Cat类定义了一个属性age, 值为1
        name = "猫" # 这里离的name不是属性, 是方法内部定义的一个局部变量name
        age = 1   # 这里的age不是属性, 是方法内部定义的一个局部变量age

c = Cat() # 实例化
c.drink() # 调用对象的方法
c.eat()  # 调用对象的方法
c.set() # 调动对象的set方法  # 因为在set方法里面有定义属性name和age的语句, 所以只有执行了set方法, 对象才会有属性
print(c.name)  # 显示对象name属性的值
print(c.age)  # 显示对象age属性的值

self的第二个作用-在定义属性方法之外的其他方法中使用属性

# 在其他方法中使用属性
self.属性名
# Cat有eat和drink两个方法
# Cat有name属性
# Cat添加一个方法, set, 定义name属性
class Cat: # 定义一个Cat类
    def eat(self): # 定义一个方法eat
        print(f"{self.name}吃") # 在eat方法中使用属性name

    def drink(self):  # 定义一个方法drink
        print(f"{self.name}喝")  # 在eat方法中使用属性name

    def set(self):
        self.name = "小猫" # 给Cat类定义了一个属性name, 值为"小猫"

c = Cat() # 实例化
c.set() # 因为set中定义了属性name
c.drink()
c.eat()

self的第三个作用-在一个方法内部调用另一个方法

# 一个方法内部, 调用另外一个方法
self.方法名()
class Cat: # 定义一个Cat类
    def eat(self): # 定义一个方法eat
        print("吃")

    def my_func(self): # 定义了一个方法my_func,
        self.eat()  # 在方法内部调用eat方法


c = Cat() # 实例化
c.my_func()

self作用-小结

  • 作用1--定义属性
  • 作用2--在方法内部使用属性
  • 作用3--在方法内部调用方法
  • self只能在类的内部使用, 不能在类的外部使用
'''
定义cat类
有set方法, set方法中定义属性name
有eat方法, 使用属性name
定义my_func方法, 方法中调用eat方法
'''
class Cat: # 定义一个Cat类
    def set(self):  # 定义一个set方法
        self.name = "小猫"  # 定义一个name属性, 值为"小猫"

    def eat(self): # 定义一个eat方法
        print(f"{self.name}爱吃鱼") # 在eat方法内部使用属性name

    def my_func(self): # 定义一个方法my_func
        self.eat() # 在my_func方法内部调用方法eat

c = Cat() # 把类Cat实例化对象c
c.set()  # 调用对象c的set方法
c.my_func() # 调用对象c的方法my_func
# cat, 有方法set, 定义属性name
# 方法eat, 使用属性name

class Cat: # 定义一个类Cat
    def set(self, name): # 定义一个方法set, 有一个形参name
        self.name = name  # 定义一个属性name, 值等于形参name的值

    def eat(self): # 定义一个eat方法
        print(f"{self.name}吃")

c1 = Cat() # 把Cat实例化为对象c1
c1.set("小猫")  # 调用对象c1的set方法,实参为小猫
c1.eat()  # 调用c1的eat方法

c2 = Cat() # 把Cat实例化为对象c2
c2.set("懒猫")  # 调用对象c1的set方法,实参为懒猫
c2.eat()  # 调用c1的eat方法

''''
Cat类有两个方法, 分别是set和eat, 一个属性name, 对于cat类来讲name属性没有值
Cat类实例化了两个对象, 分别是c1和c2
c1和c2有两个方法, 分别是set和eat, 一个属性name
c1的name属性值为"小猫"
c2的name属性值为"懒猫"
c1的eat方法调用执行结果为显示"小猫吃"
c2的eat方法调用执行结果为显示"懒猫吃"
'''

初始化方法

方法名__init__

  • init的前面有两个下划线, init后面有两个下划线
  • 不能习惯性的写成int, init是initial前面四个字母, initial意思就是初始化的意思
  • 初始化方法在对象实例化的时候自动调用, 不需要通过代码明确的调用
class Cat:
    def __init__(self):
        print("我是cat类的初始化方法")

c = Cat()  # 实例化的时候__init__方法自动被调用
# c.__init__() 不用这样写代码, __init__方法不需要这样调用
  • 初始化方法一个作用
    • 在初始化方法中定义类的属性
    • 在对象实例化的同时, 属性就自动定好好, 并不需要额外的提供定义属性的方法
  • 一个类中的属性, 一般都是在init方法中定义, 而不是其他方法中定义
'''
定义一个cat类
有方法eat
有属性name
有属性set
'''

'''
class Cat:
    def set(self): # set方法只有一个作用, 就是定义属性name
        self.name = "小猫"
    def eat(self):
        print(f"{self.name}爱吃鱼")

c = Cat()
c.set()
c.eat() # 在调用eat方法前, 必须先调用set方法
# 类的不足, 在调用eat方法前, 必须先调用set, 使用起来不方便
'''
class Cat:
    def __init__(self): # 在初始化方法总定义name
        self.name = "小猫"
    def eat(self):
        print(f"{self.name}爱吃鱼")

c = Cat() # 实例化的时候, __init__被自动调用, 带来的好处是, 实例化的时候, 属性name会自动出现
c.eat()

带有形参的初始化方法

  • 初始化方法的形参主要作用是给属性赋值的
class Cat:
    def __init__(self, name): # 初始化方法的形参name
        self.name = name  # 属性name的值等于形参name的值
    def eat(self):
        print(f"{self.name}爱吃鱼")

c1 = Cat("小猫") # 实参"小猫"实际是给了初始化方法的形参name
c1.eat()

c2 = Cat("懒猫")
c2.eat()
# 一个Cat类有两个对象c1和c2, 两个对象都有name属性和eat方法
# c1的name属性值为小猫
# c2的name属性值为懒猫

image

初始化方法的缺省参数

class Cat:
    def __init__(self, name = "小猫", age = 1):
        self.name = name  # 属性name的值等于形参name的值
        self.age = age
    def show(self):
        print(f"我的名字叫{self.name}, 我今年{self.age}岁")

c1 = Cat() # __init__方法的形参name和age将采用缺省值
c1.show() # 调用对象c的show方法

c2 = Cat("肥猫") # 只有age缺省, name采用实参的值
c2.show()

c3 = Cat("肥猫", 10)  # name和age都放弃缺省, 都采用实参的值
c3.show()

c4 = Cat(age = 5)  # name缺省, age的值为5
c4.show()
# 以上四个对象都有name和age属性, 都有show方法
# 四个对象的name和age属性的值是不同的

dir函数

  • 显示对象所有的属性和方法
dir(对象)
class Cat:
    def __init__(self, name = "小猫", age = 1):
        self.name = name  # 属性name的值等于形参name的值
        self.age = age
    def show(self):
        print(f"我的名字叫{self.name}, 我今年{self.age}岁")

c1 = Cat()

print(dir(c1))  # 显示一个对象所有的属性和方法

__str__方法

  • __str__必须有return返回值, 返回值必须是字符串
  • __str__方法除了self形参外, 不能有其他的形参
  • 把一个对象直接放到print里面, 显示结果为__str__的返回值
class Cat:
    def __str__(self):
        return "我是Cat类的实例"

c = Cat()
print(c) # 把对象c直接放到print里显示, 显示的就是__str__方法返回的字符串

__del__方法

  • 也叫销毁方法
  • 对象在内存中消失的时候, 自动调用
  • __del__方法除了self,没有其他形参, 也没有返回值
class Cat:
    def __init__(self):
        print("初始化方法调用了")

    def __del__(self):
        print("销毁方法调动了")

c = Cat() # 自动调用了__init__方法, 初始化方法
# c是个对象, 道理和变量一样, 由于是在函数外定义的, 所以全局的
print("程序结束")
# 到这里, 所有代码执行完了, 对象c要在内存中消失, 消失的时候, 会自动调用__del__销毁方法
# 程序的执行顺序
'''
初始化方法调用了
程序结束
销毁方法调动了
'''

设计类的基本套路

  • 类的所有属性都在__init__方法中定义
  • 类有几个属性__init__方法就有几个形参, 目的是为了给属性赋初值
'''
设计了一个Person, 有属性name, age和height
有方法run和eat
'''
class Person:  # 定义一个Person类
    def __init__(self, name, age, height):
        self.name = name
        self.age = age
        self.height = height

    def run(self):
        print(f"我叫{self.name}, 今年{self.age}, 身高{self.height}, 跑步")

    def eat(self):
        print(f"我叫{self.name}, 今年{self.age}, 身高{self.height}, 吃饭")

p1 = Person("小明", 20, 1.75) # 实例化对象p1
p2 = Person("小美", 18, 1.65) # 实例化对象p2
p1.run() # 调用了p1的方法
p1.eat()
p2.run()  # 调用了p2的方法
p2.eat()

总结

image

posted @ 2022-05-24 20:55  鬼谷仙生  阅读(36)  评论(0编辑  收藏  举报