一、面向对象

1、类与对象

类名要满足大驼峰命名法,每一个单词首字母都大写

(使用dir内置函数,可以查看对象内所有的属性和方法)

__方法名__ 格式的方法是 Python 提供的 内置方法 / 属性

序号方法名类型作用
01 __new__ 方法 创建对象时,会被 自动 调用
02 __init__ 方法 对象被初始化时,会被 自动 调用
03 __del__ 方法 对象被从内存中销毁前,会被 自动 调用
04 __str__ 方法 返回对象的描述信息print 函数输出使用

类中的方法第一个参数必须是self

class Cat:

    def eat(self):
        print("小猫爱吃鱼")

    def drink(self):
        print("小猫要喝水")


tom = Cat()
tom.eat()
tom.drink()

在Python中想给对象增加属性,只需要在 类的外部的代码 中直接通过 . 设置一个属性即可,这种方式虽然简单,但是不推荐使用;

class Cat:

    def eat(self):
        print("小猫%s爱吃鱼" % self.name)

    def drink(self):
        print("小猫%s要喝水" % self.name)


tom = Cat()
tom.name = "汤姆"
tom.eat()
tom.drink()

 

2、初始化方法

当使用类名()创建对象时,会自动执行:为对象在内存中分配空间、为对象的属性设置初始值;

__init__方法是专门用来定义一个类具有哪些属性的方法,是对象的内置方法;

class Cat:
    def __init__(self):
        print("初始化方法")
        self.name = ""

    def eat(self):
        print("小猫%s爱吃鱼" % self.name)

    def drink(self):
        print("小猫%s要喝水" % self.name)


tom = Cat()
tom.eat()
tom.drink()
class Cat:
    def __init__(self, name):
        print("初始化方法")
        self.name = name

    def eat(self):
        print("小猫%s爱吃鱼" % self.name)


tom = Cat("Tom")
tom.eat()

 

3、内置方法

当一个对象从内存中被销毁前,会自动调用__del__方法,对象的生命周期结束

当使用print打印对象变量时,如果希望打印自定义的内容,就可以利用__str__这个内置方法,__str__必须要返回一个字符串

class Cat:

    def __init__(self, name):
        self.name = name
        print("%s来了" % self.name)

    def __del__(self):
        print("%s走了" % self.name)

    def __str__(self):
        return "我是一只小猫,我叫%s" % self.name


tom = Cat("tom")
print(tom)

 

4、封装案例

class Person:

    def __init__(self, name, weight):
        self.name = name
        self.weight = weight

    def run(self):
        self.weight -= 0.5

    def eat(self):
        self.weight += 1

    def __str__(self):
        return "我叫%s,我的体重是%.1f" % (self.name, self.weight)


xiaomei = Person("小美", 90.0)
xiaomei.run()
xiaomei.eat()
print(xiaomei)
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):
        if item.area > self.free_area:
            print("%s面积太大,无法添加" % item.name)
            return
        self.item_list.append(item.name)
        self.free_area -= item.area


bed = HouseItem("席梦思", 10)
print(bed)
chest = HouseItem("衣柜", 5)
print(chest)
table = HouseItem("餐桌", 5)
print(table)

my_home = House("两室一厅", 100)
my_home.add_item(bed)
my_home.add_item(chest)
my_home.add_item(table)
print(my_home)
class Gun:

    def __init__(self, type):
        self.type = type
        self.bullet_count = 0

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

    def shoot(self):
        if self.bullet_count <= 0:
            print("没有子弹了")
            return
        self.bullet_count -= 3
        print("[%s]突突突...[%d]" % (self.type, self.bullet_count))


class Soldier:

    def __init__(self, name):
        self.name = name
        self.gun = None

    def fire(self):
        # 使用身份运算符is,用于比较对象的内存地址是否一致
        if self.gun is None:
            print("%s还没有枪" % self.name)
            return
        print("冲啊...[%s]" % self.name)
        self.gun.add_bullet(50)
        self.gun.shoot()


AK47 = Gun("AK47")
# AK47.add_bullet(50)
# AK47.shoot()

xusanduo = Soldier("许三多")
xusanduo.gun = AK47
xusanduo.fire()
运算符描述实例
is is 是判断两个标识符是不是引用同一个对象 x is y,类似 id(x) == id(y)
is not is not 是判断两个标识符是不是引用不同对象 x is not y,类似 id(a) != id(b)

 

5、私有属性和方法

 在定义属性或方法时,在属性名或者方法名前增加两个下划线,定义的就是私有属性或方法

class Woman:

    def __init__(self, name):
        self.name = name
        self.__age = 18

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


xiaoyuan = Woman("小圆")
xiaoyuan.secret()

在Python中并没有真正意义的私有(伪私有属性/方法),Python解释器会对私有属性和方法名做特殊处理:_类名__属性名

class Woman:

    def __init__(self, name):
        self.name = name
        self.__age = 18

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


xiaoyuan = Woman("小圆")
print(xiaoyuan._Woman__age)
xiaoyuan._Woman__secret()

 

6、单继承

面向对象三大特性:

封装:根据职责将属性和方法封装到一个抽象的类中

继承:实现代码的重用,相同的代码不需要重复的编写

多态:不同的对象调用相同的方法,产生不同的执行结果,增加代码的灵活度

子类-父类-继承

派生类-基类-派生

class Animal:
    def eat(self):
        print("")

    def drink(self):
        print("")

    def run(self):
        print("")


class Dog(Animal):
    def bark(self):
        print("")


class XiaoTianQuan(Dog):
    def fly(self):
        print("")


wangcai = XiaoTianQuan()
wangcai.eat()
wangcai.run()
wangcai.bark()
wangcai.fly()

 

7、方法的重写

class Animal:
    def eat(self):
        print("")

    def run(self):
        print("")


class Dog(Animal):
    def bark(self):
        print("汪汪汪")


class XiaoTianQuan(Dog):
    def fly(self):
        print("")

    def bark(self):
        super().bark()
        print("哈哈哈")


wangcai = XiaoTianQuan()
wangcai.eat()
wangcai.bark()
wangcai.fly()

super()就是使用super类创建出来的对象

在Python2时,如果需要调用父类的方法,还可以使用以下方式:父类名.方法(self)(不推荐使用) 

 

8、间接访问父类私有属性和方法

通过父类公有方法间接访问父类的私有属性和私有方法

class A:
    def __init__(self, num1):
        self.num1 = num1
        self.__num2 = 2

    def __test(self):
        print("私有方法")

    def test(self):
        print(self.__num2)
        self.__test()


class B(A):
    pass


b = B(1)
b.test()

 

9、多继承

class A:
    def test(self):
        print("test方法")


class B:
    def demo(self):
        print("demo方法")


class C(A, B):
    pass


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

如果父类之间存在同名的属性或者方法,应该尽量避免使用多继承。

同名方法的调用,是根据Python中的MRO(method resolution order)方法搜索顺序,来决定的;

Python提供的__mro__可以查看方法搜索顺序:

print(C.__mro__)

输出:  (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)

  • 在搜索方法时,是按照 __mro__ 的输出结果 从左至右 的顺序查找的
  • 如果在当前类中 找到方法,就直接执行,不再搜索
  • 如果 没有找到,就查找下一个类 中是否有对应的方法,如果找到,就直接执行,不再搜索
  • 如果找到最后一个类,还没有找到方法,程序报错

 

10、新式类与经典类

新式类:以object为基类,推荐使用,Python3中没有指定父类,会默认以object为基类,Python3中的类都是新式类

旧式类:不以object为基类,不推荐使用

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

为了保证代码能在Python2/3运行,编写代码时,如果没有父类,建议统一继承自object

 

11、多态

不同的子类对象调用相同的父类方法,产生不同的执行结果

以继承和重写父类方法为前提

class Dog(object):
    def __init__(self, name):
        self.name = name

    def game(self):
        print("%s蹦蹦跳跳" % self.name)


class XiaoTianDog(Dog):
    def game(self):
        print("%s飞天" % self.name)


class Person(object):
    def __init__(self, name):
        self.name = name

    def game_with_dog(self, dog):
        print("%s与%s快乐的玩耍" % (self.name, dog.name))
        dog.game()


xtq = XiaoTianDog("哮天犬")
wc = Dog("旺财")
xm = Person("小明")

xm.game_with_dog(xtq)
xm.game_with_dog(wc)

传入不同的狗对象实参,就会产生不同的执行效果

 

12、类属性和类方法

每一个对象 都有自己 独立的内存空间,保存各自不同的属性;

多个对象的方法,在内存中只有一份,在调用方法时,需要把对象的引用 传递到方法内部;

类是一个特殊的对象:类对象在内存中只有一份,在程序运行时,类同样会被加载到内存

类对象拥有自己的属性和方法,直接通过 类名. 的方式访问

 

类属性:记录与这个类相关的特征

class Tool(object):
    # 记录创建工具对象的个数
    count = 0

    def __init__(self, name):
        self.name = name
        Tool.count += 1


t1 = Tool("锤子")
t2 = Tool("钳子")
t3 = Tool("螺丝刀")
print(Tool.count)

Python中属性的获取存在一个向上查找机制,首先在对象内部查找对象属性,没有找到就会向上寻找类属性;

因此,访问类属性可以用:

类名.类属性

对象.类属性(不推荐)

如果使用 对象名.类属性 = 值 赋值语句,只会 给对象添加一个属性,而不会影响到 类属性的值

 

类方法:类方法内部可以直接访问类属性,或者调用其他的类方法

类方法的第一个参数必须是cls,和self类似,调用方法时不需要传入cls参数,在方法内部可以通过cls访问类属性和其他的类方法;

class Tool(object):
    count = 0

    def __init__(self, name):
        self.name = name
        Tool.count += 1

    @classmethod
    def show_count(cls):
        print(cls.count)


t1 = Tool("锤子")
t2 = Tool("钳子")
t3 = Tool("螺丝刀")
Tool.show_count()

 

13、静态方法

静态方法:既不需要访问类属性/类方法,又不需要访问实例属性或实例方法

class Dog(object):

    @staticmethod
    def run():
        # 既不需要访问实例属性又不需要访问类属性
        # 不需要传递参数
        print("小狗要跑...")


# 通过类名调用静态方法,不需要创建对象
Dog.run()

 

14、方法综合实例

class Game(object):

    top_score = 0

    def __init__(self, player_name):
        self.player_name = player_name

    @staticmethod
    def help():
        print("这是游戏帮助信息")

    @classmethod
    def show_top(cls):
        print("历史最高分:%d" % cls.top_score)

    def start_game(self):
        print("欢迎你 %s" % self.player_name)
        score = int(input("游戏结束,你的分数是:"))
        if score > Game.top_score:
            Game.top_score = score


Game.help()
Game.show_top()

p1 = Game("John Snow")
p1.start_game()
Game.show_top()

p2 = Game("Alla Stark")
p2.start_game()
Game.show_top()

实例方法:方法内部需要访问实例属性,而且可以使用 类名.属性名 访问类属性

类方法:方法内部只需要访问类属性

静态方法:方法内部不需要访问属性

 

15、单例模式

让类创建的对象,在系统中只有唯一的实例;

每一次执行 类名() 返回的对象,内存地址都是相同的;

应用场景:音乐播放器对象、回收站对象、打印机对象;

 

使用类名创建对象时,Python解释器会首先调用__new__方法为对象分配空间;

__new__是一个由object基类提供的内置的静态方法,主要作用有两个:在内存中为对象分配空间,返回对象的引用

Python解释器获得对象引用后,将引用作为第一个参数,传递给__init__方法;

重写 __new__ 方法 一定要 return super().__new__(cls) ,否则 Python 的解释器 得不到 分配了空间的 对象引用,就不会调用对象的初始化方法

注意:__new__ 是一个静态方法,在调用时需要主动传递cls 参数

class MusicPlayer(object):
    def __new__(cls, *args, **kwargs):
        # 创建对象时,new方法会被自动调用
        print("分配内存空间")
        # 返回对象引用,传递给init方法
        return super().__new__(cls)

    def __init__(self):
        print("播放器初始化")


player = MusicPlayer()
print(player)

 

单例模式的实现:

①定义一个 类属性,初始值是 None,用于记录 单例对象的引用

②重写 __new__ 方法

③如果 类属性is None,调用父类方法分配空间,并在类属性中记录结果

④返回 类属性 中记录的 对象引用

class MusicPlayer(object):
    instance = None

    def __new__(cls, *args, **kwargs):
        if cls.instance is None:
            cls.instance = super().__new__(cls)
        return cls.instance

    def __init__(self):
        print("初始化代码")


player1 = MusicPlayer()
print(player1)
player2 = MusicPlayer()
print(player2)

 

只初始化一次:

class MusicPlayer(object):
    instance = None
    init_flag = False

    def __new__(cls, *args, **kwargs):
        if cls.instance is None:
            cls.instance = super().__new__(cls)
        return cls.instance

    def __init__(self):
        if MusicPlayer.init_flag is False:
            print("初始化对象")
            MusicPlayer.init_flag = True


player1 = MusicPlayer()
print(player1)
player2 = MusicPlayer()
print(player2)

 

posted on 2019-06-19 10:32  三分天涯  阅读(251)  评论(0编辑  收藏  举报