面向对象
面向对象的介绍
- 面向过程:根据业务逻辑从上到下写代码
- 面向对象:将变量和函数绑定到一起,分类进行封装,每个程序只要负责分配给自己的分类,这样能够更快速的开发程序,减少了重复代码。
面向过程编程最易被初学者接受,其往往用一长段代码来实现指定功能,开发过程的思路是将数据和函数按照执行的逻辑顺序组织在一起,数据与函数分开考虑,面向过程基本是由函数组成的。
# 面向过程
# 关注点:怎样做
def add_user():
pass
def del_user():
pass
def modify_user():
pass
def query_user():
pass
def show_all():
pass
def start():
pass
if __name__ == '__main__':
start()
# 面向对象编程(Object Oriented Programming, OOP, 面向对象程序设计)
# 关注点:谁来做
# 例如,植物大战僵尸
# 分类:
# 大方向分为两类,植物和僵尸
# Plant:1.特征:hp,cost,position,cd 2.行为:shake()
# 类的继承:ProtectPlant, AttackPlant:damage伤害值,attack_cd, attack()
# Zombie:1.特征:2.行为:
面向对象的基本语法
类和对象是面向对象编程的两个核心概念。
- 类
类是对一群具有相同特征或者行为的事物的一个统称,是抽象的。
- 特征其实就是一个变量,在类里称之为属性
- 行为其实就是一个函数,在类里称之为方法
- 类其实就是由属性和方法组成的一个抽象概念
- 对象
对象是由类创建出来的一个具体存在。由哪一个类创建出来的对象,就拥有在哪一个类中定义的属性和方法。对象就相当于用图纸制造的飞机。在开发中,应该先有类,在类里定义好属性和行为,再根据类来创建对象。
在程序开发中,要设计一个类,通常需要满足三个要素:
-
类名:这类事物的名字,按照大驼峰命名法起名
- 属性:这类事物具有什么样的特征
3. 方法:这类事物具有什么样的行为
- 属性:这类事物具有什么样的特征
# 小明今年18岁,身高1.75,每天早上跑完步,会去吃东西
# 小美今年17岁,身高1.65,小美不跑步,小美喜欢吃东西
# 定义类:类名怎样定义?使用class定义一个类
# class 类名 类名一般需要遵守大驼峰命名法,每一个单词的首字母都大写
# 1. class <类名>:
# 2. class <类名>(object):
class Student(object): # 关注这个类有哪些特征和行为
# 在 __init__ 方法里,以参数的形式定义特征,称之为属性
def __init__(self, name, age, height):
self.name = name
self.age = age
self.height = height
# 行为定义为一个个函数
def run(self):
print('正在跑步')
def eat(self):
print('正在吃东西')
# 使用 Student 类创建了两个实例对象 s1 s2
# s1 和 s2 都会有name,age,height属性,同时都有run和eat方法
s1 = Student('小明', 18, 1.75) # Student() ==> 会自动调用 __init__ 方法
s2 = Student('小美', 17, 1.65)
# 根据业务逻辑,让不同的对象执行不同的行为
s1.run()
s1.eat()
s2.eat()
self的介绍
class Student(object):
def __init__(self, x, y):
self.name = x
self.age = y
def say_hello(self):
print('大家好,我是', self.name)
# Student('张三', 18) 这段代码具体做了什么呢?
# 1. 调用 __new__ 方法,用来申请一段内存空间
# 2. 调用 __init__ 方法传入参数,并让 self 指向申请好的那段内存空间,填充数据
# 3. 让 s1 也指向创建好的这段内存空间
s1 = Student('张三', 18)
print('0x%X' % id(s1)) # 0x18B224DE2C8
print('s1的名字是', s1.name) # s1的名字是 张三
s1.say_hello() # 大家好,我是 张三
s2 = Student('jack', 21)
s2.say_hello() # 大家好,我是 jack
双下划线slots双下划线属性的使用
class Student(object):
# 这个属性直接定义在类里,是一个元组,用来规定对象可以存在的属性
__slots__ = ('name', 'age', 'city')
def __init__(self, x, y):
self.name = x
self.age = y
def say_hello(self):
print('大家好,我是', self.name)
s = Student('张三', 18)
print(s.name)
s.say_hello()
# 没有属性会报错
# print(s.height) # AttributeError: 'Student' object has no attribute 'height'
# 直接使用等号给一个属性赋值
# 如果这个属性以前不存在,会给对象添加一个新的属性
# 动态属性
s.city = '上海' # 给对象添加了一个city属性
print(s.city) # 上海
# 如果这个属性以前存在,会修改这个属性对应的值
s.name = 'jack'
print(s.name) # jack
魔法方法1
# 魔法方法,也叫魔术方法,是类里的特殊的一些方法
# 特点:
# 1. 不需要手动调用,会在合适的时机自动调用
# 2. 这些方法,都是使用 __ 开始,使用 __ 结束
# 3. 方法名都是系统规定好的,在合适的时机自己调用
import time
class Person(object):
# 在创建对象时,会自动调用这个方法
def __init__(self, name, age):
print('__init__方法被调用了')
self.name = name
self.age = age
def __del__(self):
# 当对象被销毁时,会自动调用这个方法
print('__del__方法被调用了')
p = Person('zhangsan', 18) # __init__方法被调用了
# del p # 也可以手动销毁
time.sleep(10) # 休息10秒,程序结束,数据自动被销毁,# __del__方法被调用了
# import datetime
#
# x = datetime.datetime(2020, 2, 24, 16, 17, 45, 200)
# print(x) # __str__ 方法, __str__ 可读性更高
# print(repr(x)) # __repr__ 方法, __repr__ 更加准确
class Person(object):
# 在创建对象时,会自动调用这个方法
def __init__(self, name, age):
print('__init__方法被调用了')
self.name = name
self.age = age
def __del__(self):
# 当对象被销毁时,会自动调用这个方法
print('__del__方法被调用了')
def __repr__(self):
return 'hello'
def __str__(self):
return '姓名:{},年龄:{}'.format(self.name, self.age)
def __call__(self, *args, **kwargs):
print('__call__ 被调用了')
# args 是一个元组,保存(1, 2)
# kwargs 是一个字典{'fn':lambda x, y: x + y}
print('args={}, kwargs={}'.format(args, kwargs))
# fn = kwargs['fn']
# return fn(args[0], args[1])
return kwargs['fn'](args[0], args[1])
p = Person('zhangsan', 18)
# 如果不做任何的修改,直接打印一个对象,是文件的 __name__.类型 内存地址
# print(p) # <__main__.Person object at 0x0000018EC3EB0408>
# 当打印一个对象的时候,会调用这个对象的 __str__ 或者 __repr__ 方法
# 如果两个方法都写了,选择 __str__ 方法
print(p)
# print(repr(p)) # 调用内置函数repr会触发对象的 __repr__ 方法
# print(p.__repr__()) # 魔法方法,一般不手动调用,但可以手动调用
# p() # 'Person' object is not callable
# 使用 __call__ 方法
# p() # 对象名() ==> 调用这个对象的 __call__ 方法
n = p(1, 2, fn=lambda x, y: x + y) # 相当于 p.__call__(可变参数) __call__ 中有可变参数
print(n)
运算符相关的魔法方法
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
def __eq__(self, other):
print('__eq__方法被调用了,other=', other)
# if self.name == other.name and self.age == other.age:
# return True
# return False
return self.name == other.name and self.age == other.age
p1 = Person('zhangsan', 18)
p2 = Person('zhangsan', 18)
p3 = Person('zhangsan', 19)
# p1 和 p2 是同一个对象吗?不是,指向了不同的地址,但内容相同
# 怎样比较两个对象是否是同一个对象?比较的是内存地址
print('0x%X' % id(p1)) # 0x2B5553586C8
print('0x%X' % id(p2)) # 0x2B555358708
# is 身份运算符 可以用来判断两个对象是否是同一个对象
print('p1 is p2', p1 is p2) # False
# __eq__ 如果不重写,默认比较依然是内存地址
print('p1 == p2', p1 == p2) # 本质调用 p1.__eq__(p2),获取这个方法的返回结果
print('p1 == p3', p1 == p3)
# is 比较两个对象的内存地址
# == 会调用对象的 __eq__ 方法,获取这个方法的比较结果
# nums1 = [1, 2, 3]
# nums2 = [1, 2, 3]
# print(nums1 is nums2) # False
# print(nums1 == nums2) # True
练习(important)
class House(object):
def __init__(self, house_type, total_area, fru_list=None):
if fru_list is None:
fru_list = []
self.house_type = house_type
self.total_area = total_area
self.free_area = total_area * 0.6
self.fru_list = fru_list
def add_fru(self, x):
if self.free_area < x.area:
print('剩余面积不足,放不进去了')
else:
self.fru_list.append(x.name)
self.free_area -= x.area
def __str__(self):
return '户型={},总面积={},剩余面积={},家具列表={}'.format(self.house_type, self.total_area, self.free_area, self.fru_list)
class Furniture(object):
def __init__(self, name, area):
self.name = name
self.area = area
house = House('一室一厅', 20)
sofa = Furniture('沙发', 10)
bed = Furniture('席梦思', 4)
chest = Furniture('衣柜', 2)
table = Furniture('餐桌', 1.5)
house.add_fru(sofa)
house.add_fru(bed)
house.add_fru(chest)
house.add_fru(table)
print(house) '''剩余面积不足,放不进去了
剩余面积不足,放不进去了
户型=一室一厅,总面积=20,剩余面积=0.0,家具列表=['沙发', '衣柜']'''
魔法方法2
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __eq__(self, other):
return self.name == other.name and self.age == other.age
# def __ne__(self, other): 使用 != 运算符会自动调用这个方法
def __gt__(self, other): # greater than 使用 > 会自动调用这个方法
return self.age > other.age
# def __ge__(self, other): # 使用 >= 运算符会自动调用
# def __lt__(self, other): # less than p1 < p2
# def __le__(self, other): # <=
def __add__(self, other): # +
return self.age + other.age
def __sub__(self, other): # -
return self.age - other
def __mul__(self, other): # *
return self.name * other
# def __truediv__(self,other): # /
# def __mod__(self, other): # 取模
def __pow__(self, power, modulo=None): # 幂运算
return self.age ** power
def __str__(self):
return 'hello'
def __int__(self):
return 20
def __float__(self):
return 100.5
p1 = Person('zhangsan', 18)
p2 = Person('zhangsan', 18)
p3 = Person('lisi', 19)
print(p1 is p2) # False
# == 运算符本质其实是调用对象的 __eq__ 方法,获取 __eq__ 方法的返回结果
# a == b => a.__eq__(b)
print(p1 == p2) # True p1.__eq__(p2)
# != 本质是调用 __ne__ 方法 或者 __eq__ 取反
print(p1 != p2) # False
print(p1 > p3) # False
print(p1 + p2) # 36
print(p1 - 2) # 16
print(p1 * 2) # zhangsanzhangsan
print(p1 ** 2) # 324
# 转换成为字符串,默认会转换成为类型 + 内存地址
# str()将对象转换成为字符串,会自动调用 __str__ 方法
# print(str(p1)) # <__main__.Person object at 0x000001CE181A1C08>
print(str(p1)) # hello
# int() ==> 调用对象的 __int__ 方法
print(int(p1)) # 20
# float() ==> 调用对象的 __float__ 方法
print(float(p1)) # 100.5
内置属性
class Person(object):
"""
这是一个人类
"""
# __slots__ = ('name', 'age')
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self):
print(self.name + '正在吃饭')
# ‘name’: 'zhangsan', 'age': 18, 'eat': <function>
p = Person('张三', 18)
print(dir(p))
print(p.__class__) # <class '__main__.Person'>
# __dict__ 把对象属性和值转换成为一个字典
print(p.__dict__) # {'name': '张三', 'age': 18}
print(p.__dir__()) # 等价于 dir(p)
print(p.__doc__) # 这是一个人类 对象名.__doc__
print(Person.__doc__) # 这是一个人类 类名.__doc__
print(p.__module__) # __main__
把对象当做字典操作
class Person(object):
def __init__(self, name, age, city):
self.name = name
self.age = age
self.city = city
def __setitem__(self, key, value):
# print('setitem方法被调用了,key={},value={}'.format(key, value))
self.__dict__[key] = value
def __getitem__(self, item):
return self.__dict__[item]
p = Person('张三', 18, '襄阳')
print(p.__dict__) # 将对象转换成为字典 {'name': '张三', 'age': 18, 'city': '襄阳'}
# 不能直接把一个对象当做字典来使用
p['name'] = 'tony' # [] 语法会调用对象的 __setitem__ 方法
p['age'] = 20
print(p.name, p.age) # jack
print(p['name']) # 会调用 __getitem__ 方法 tony
类属性和对象属性
class Person(object):
type = '人类' # 这个属性定义在类里,函数之外,我们称之为类属性
def __init__(self, name, age):
self.name = name
self.age = age
# Person 称之为 类对象
# 对象 p1, p2 是通过 Person 类创建出来的 实例对象
# name 和 age 是对象属性,在 __init__ 方法里,以参数的形式定义的
# 每一个实例对象都会单独保存一份的属性
# 每个实例对象之间的属性没有关联,互不影响
p1 = Person('张三', 18)
p2 = Person('李四', 19)
# 类属性可以通过类对象和实例对象获取
print(Person.type) # 人类
print(p1.type) # 人类
print(p2.type) # 人类
p1.type = 'human'
print(p1.type) # 并不会修改类属性,而是给实例对象添加了一个新的对象属性
print(Person.type) # 人类
# 类属性只能通过类对象来修改,实例对象无法修改类属性
Person.type = 'monkey' # 修改了类属性
print(p2.type) # monkey
私有属性和方法的使用
import datetime
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
self.__money = 1000 # 以 __ 开始的变量是私有变量
# def test(self):
# self.__money += 10 # 在这里可以访问私有属性
def get_money(self):
print('{}查询了余额'.format(datetime.datetime.now()))
return self.__money
def set_money(self, qian):
if type(qian) != int:
print('设置的余额不合法')
else:
print('修改余额了')
self.__money = qian
def __demo(self): # 以 __ 开始的函数,是私有函数,在外部无法调用
print('我是demo函数,name={}'.format(self.name))
def test(self):
self.__demo()
p = Person('张三', 18)
print(p.name, p.age) # 张三 18
# print(p.__money) # 不能够直接获取到私有变量
# p.__demo() # 不能直接调用demo函数,它是私有方法
p._Person__demo()
# 获取私有变量的方式:
# 1. 使用 对象._类名__私有变量名 获取
# p.test()
print(p._Person__money) # 1000
# 2. 定义 get 和 set 方法来获取
print(p.get_money()) # 1000
p.set_money('hello')
print(p.get_money())
类方法和静态方法
class Calculator(object):
@staticmethod
def add(a, b):
return a + b
@staticmethod
def minus(a, b):
return a - b
@staticmethod
def mul(a, b):
return a * b
# c = Calculator()
# c.add(1, 2)
Calculator.add(1, 4)
Calculator.minus(9, 2)
Calculator.mul(2, 4)
class Person(object):
type = 'human'
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self, food): # 对象方法有一个参数self,指的是实例对象
print(self.name + '正在吃' + food)
# 如果一个方法里没有用到实例对象的任何属性,可以将这个方法写成static
@staticmethod
def demo(): # 默认的方法都是对象方法
print('hello')
@classmethod
def test(cls): # 如果这个函数只用到了类属性,我们可以把它定义成为一个类方法
# 类方法会有一个参数 cls,也不需要手动的传参,会自动传参
# cls 指的是类对象 cls is Person ==> True
print(cls.type + 'yes')
p1 = Person('张三', 18)
p2 = Person('李四', 19)
# 实例对象在调用方法时,不需要给形参self传参,会自动的把实例对象传递给self
# eat 对象方法,可以直接使用 实例对象.方法名(参数) 调用
# 使用 对象名.方法名(参数) 调用的方式,不用传self
p1.eat('红烧牛肉泡面') # 直接使用实例对象调用方法
# print(p1.eat is p2.eat) # False
# 方法存在类对象中,方法和实例对象是绑定关系
# print(p1.eat) # <bound method Person.eat of <__main__.Person object at 0x000001946CF41B08>>
# print(p2.eat) # <bound method Person.eat of <__main__.Person object at 0x000001946CF41C08>>
# print(Person.eat) # <function Person.eat at 0x00000297A5D69558>
# 对象方法还可以使用类对象来调用 类名.方法名()
# 这种方式,不会自动给self传参,需要手动的指定self
Person.eat(p2, '西红柿鸡蛋盖饭')
# 静态方法:没有用到实例对象,也用不到类对象
Person.demo()
p1.demo()
# 类方法:可以使用实例对象和类对象调用
p1.test()
Person.test()
单例设计模式
class Singleton(object):
__instance = None # 类属性
__is_first = True
def __new__(cls, *args, **kwargs):
if cls.__instance is None:
# 申请内存,创建一个对象,并把对象的类型设置为cls
cls.__instance = object.__new__(cls)
return cls.__instance
def __init__(self, a, b):
if self.__is_first:
self.a = a
self.b = b
self.__is_first = False
# 调用 __new__ 方法申请内存
# 如果不重写 __new__ 方法,会调用 object 的 __new__ 方法
# object的 __new__ 方法会申请内存
# 如果重写了 __new__ 方法,需要自己手动的申请内存
s1 = Singleton('呵呵', '嘿嘿嘿')
s2 = Singleton('哈哈', '嘻嘻嘻')
s3 = Singleton('嘎嘎', '嘤嘤嘤')
# 单例就是使得结果为 True,创建的对象一样
print(s1 is s2) # True
print(s1.a, s1.b) # 嘎嘎 嘤嘤嘤 这样前一个会被覆盖
# 能否避免被覆盖,那就改 __init__
print(s1.a, s1.b) # 呵呵 嘿嘿嘿
小练习
# 定义一个类属性,记录通过这个类创建了多少个对象
class Person(object):
__count = 0 # 类属性
def __new__(cls, *args, **kwargs):
cls.__count += 1
instance = object.__new__(cls) # 申请内存,创建了一个对象,并设置类型是 Person 类
return instance
def __init__(self, name, age):
# Person.count += 1
self.name = name
self.age = age
@classmethod
def get_count(cls):
return cls.__count
# 每次创建对象,都会调用 __new__ 和 __init__ 方法
# 调用 __new__ 方法,用来申请内存
# 如果不重写 __new__ 方法,它会自动找 object 的 __new__
# object 的 __new__ 方法,默认实现申请一段内存,创建一个对象
p1 = Person('张三', 18)
p2 = Person('李四', 19)
p3 = Person('jack', 20)
# print(Person.get_count()) # 3
# 用 __new__ 方法也可以做
print(Person.get_count()) # 3
# 申请了内存,创建了一个对象,并设置它的类型是Person
# p4 = object.__new__(Person)
# p4.__init__('tony', 23)
继承的基本使用
# 面向对象编程有三大特性:封装、继承、多态
# 封装:函数是对语句的封装;类是对函数和变量的封装
# 继承:类和类之间可以人为手动的建立父子关系,父类(基类)的属性和方法,子类(派生类)可以使用
# 多态:是一种技巧,提高代码的灵活度
class Animal(object):
def __init__(self, name, age):
self.name = name
self.age = age
def sleep(self):
print(self.name + '正在睡觉')
class Dog(Animal):
def bark(self):
print(self.name + '正在叫')
class Student(Animal):
def study(self):
print(self.name + '正在好好学习')
# Dog() 调用 __new__ 方法,再调用 __init__ 方法
# Dog 里没有 __new__ 方法,会查看父类是否重写了 __new__ 方法
# 父类里也没有重写 __new__ 方法,查找父类的父类,找到了 object
# 调用 __init__ 方法,Dog类没有实现,会自动找 Animal 父类
d1 = Dog('大黄', 3)
print(d1.name) # 父类里定义的属性,子类可以直接使用
d1.sleep() # 父类的方法子类实例对象可以直接调用
d1.bark()
s1 = Student('小明', 18)
s1.sleep()
s1.study()
继承的注意事项
class A(object):
def demo_a(self):
print('我是A类里的方法demo_a')
def foo(self):
print('我是A类里的foo方法')
class B(object):
def demo_b(self):
print('我是B类里的方法demo_b')
def foo(self):
print('我是B类里的foo方法')
# class C: # 如果不写父类,Python3以后,默认继承自object
# Python里允许多继承
class C(A, B):
pass
c = C()
c.demo_a() # 我是A类里的方法demo_a
c.demo_b() # 我是B类里的方法demo_b
# 如果两个不同的父类有同名方法,有一个类属性可以查看方法的调用顺序
c.foo() # 我是A类里的foo方法
print(C.__mro__) # (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
class A(object):
pass
class B(object):
def foo(self):
print('我是B类里的foo方法')
class C(A):
def foo(self):
print('我是C类里的foo方法')
class D(B):
pass
class X(D, C):
pass
x = X()
# 深度优先
x.foo() # 我是B类里的foo方法
print(X.__mro__) # (<class '__main__.X'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>,
# <class '__main__.A'>, <class 'object'>)
私有属性的继承特点
class Animal(object):
def __init__(self, name, age):
self.name = name
self.age = age
self.__money = 1000
def eat(self):
print(self.name + '正在吃东西')
def __test(self):
print('我是Animal类里的test方法')
class Person(Animal):
def __demo(self):
print('我是Person里的私有方法')
p = Person('张三', 18)
print(p.name)
p.eat()
p._Person__demo() # 自己类里定义的私有方法 对象名._类名__私有方法名()
p._Animal__test() # 可以通过 对象名._父类名__私有方法() 调用
# 私有属性和方法,子类不会继承
# p._Person__test() # 父类的私有方法,子类没有继承
# print(p._Person__money)
print(p._Animal__money) # 1000
新式类和经典类
# 手动的指定Student类继承自object
class Student(object):
pass
# 没有指定Dog的父类,Python3里默认继承自object
class Dog:
pass
# 新式类和经典类的概念:
# 1.新式类:继承自 object 的类我们称之为新式类
# 2.经典类:不继承自 object 的类
# 在Python2里,如果不手动的指定一个类的父类是object,这个类就是一个经典类
# Python3里不存在经典类,都是新式类
面向对象相关的运算符方法
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
class X(object):
pass
class Student(Person, X):
pass
p1 = Person('张三', 18)
p2 = Person('张三', 18)
s = Student('jack', 20)
# 获取两个对象的内存地址 id(p1) == id(p2)
print(p1 is p2) # is 身份运算符是用来比较是否是同一个对象
# type(p1) # 其实获取的就是类对象
print(type(p1) == Person) # True
# s 这个实例对象是否是由Student类创建的?
print(type(s) == Student) # True
print(type(s) == Person) # False
# isinstance 用来判断一个对象是否是由指定的类(或者父类)实例化出来的
print(isinstance(s, (Student, X))) # True
print(isinstance(s, Person)) # True
print(isinstance(p1, Student)) # False
# issubclass 用来判断一个类是否是另一个类的子类
print(issubclass(Student, (Person, X))) # True
print(issubclass(Person, Student)) # False
子类重写父类方法
# 继承特点:如果一个类A继承自类B,由类A创建出来的实例对象都能直接使用类B里定义的方法
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
def sleep(self):
print(self.name + '正在睡觉')
class Student(Person):
def __init__(self, name, age, school):
# self.name = name
# self.age = age
# 子类在父类实现的基础上,有添加了自己新的功能
# 1.父类名.方法名(self, 参数列表)
# Person.__init__(self, name, age)
# 2.使用super直接调用父类的方法。推荐使用第二种方法
super(Student, self).__init__(name, age)
self.school = school
def sleep(self):
print(self.name + '正在课间休息时睡觉')
def study(self):
print(self.name + '正在学习')
s = Student('jerry', 20, '春田花花幼稚园') # 调用了父类的 __init__ 方法
s.sleep() # 调用了父类的 sleep 方法
# 1.子类的实现和父类的实现完全不一样,子类可以选择重写父类的方法
# 2.子类在父类的基础上又有更多的实现
不使用多态的问题
class PoliceDog(object):
def attack_enemy(self):
print('警犬正在攻击坏人')
class Person(object):
def __init__(self, name, dog):
self.name = name
self.dog = dog
def work_with_pd(self):
print(self.name + '正在工作')
self.dog.attack_enemy()
pd = PoliceDog()
police = Person('张警官', pd)
police.work_with_pd()
class PoliceDog(object):
def attack_enemy(self):
print('警犬正在攻击坏人')
class BlindDog(object):
def lead_road(self):
print('导盲犬正在领路')
class DrugDog(object):
def search_drug(self):
print('缉毒犬正在搜毒')
class Person(object):
def __init__(self, name):
self.name = name
self.dog = None
def work_with_pd(self):
if self.dog is not None:
print(self.name + '正在工作')
self.dog.attack_enemy()
def work_with_bd(self):
if self.dog is not None:
self.dog.lead_road()
def work_with_dd(self):
if self.dog is not None:
self.dog.search_drug()
p = Person('张三')
pd = PoliceDog()
p.dog = pd
p.work_with_pd()
bd = BlindDog()
p.dog = bd
p.work_with_bd()
dd = DrugDog()
p.dog = dd
p.work_with_dd()
多态的使用
# 多态是基于继承,通过子类重写父类的方法,达到不同的子类对象调用相同的父类方法,得到不同的结果
# 提高代码的灵活度
class Dog(object):
def work(self):
print('狗正在工作')
class PoliceDog(Dog):
def work(self):
print('警犬正在攻击敌人')
class BlindDog(Dog):
def work(self):
print('导盲犬正在领路')
class DrugDog(Dog):
def work(self):
print('缉毒犬正在搜毒')
class Person(object):
def __init__(self, name):
self.name = name
self.dog = None
def work_with_dog(self):
if self.dog is not None and isinstance(self.dog, Dog):
self.dog.work()
p = Person('张三')
pd = PoliceDog()
p.dog = pd
p.work_with_dog()
bd = BlindDog()
p.dog = bd
p.work_with_dog()
dd = DrugDog()
p.dog = dd
p.work_with_dog()
练习题
1
# 设计两个类:
# 一个点类,属性包括x,y坐标
# 一个Rectangle类(矩形),属性有左上角和右下角的坐标
# 方法:1.计算矩形的面积;2.判断点是否在矩形内
# 实例化一个点对象,一个矩形对象,输出矩形的面积,输出点是否在矩形内
class Point(object):
def __init__(self, x: int, y: int):
self.x = x
self.y = y
class Rectangle(object):
def __init__(self, top_left: Point, bottom_right: Point):
self.top_left = top_left
self.bottom_right = bottom_right
def get_area(self):
# 面积:长*宽
length = abs(self.bottom_right.x - self.top_left.x)
width = abs(self.top_left.y - self.bottom_right.y)
return length * width
def is_inside(self, point):
return self.bottom_right.x >= point.x >= self.top_left.x and self.top_left.y >= point.y >= self.bottom_right.y
p1 = Point(4, 20) # 定义左上角的点
p2 = Point(30, 8) # 定义右下角的点
r = Rectangle(p1, p2) # 把左上角和右下角的点传递给矩形
print(r.get_area()) # 26*12=312
p = Point(20, 30)
print(r.is_inside(p))
2
# 建立一个汽车类 Auto
# 包括轮胎个数,汽车颜色,车身重量,速度等属性,并通过不同的构造方法创建实例
# 至少要求 汽车能够加速 减速 停车
# 再定义一个小汽车类 CarAuto 继承 Auto 并添加空调、导航属性
# 并且重新实现方法覆盖加速、减速的方法
class Auto(object):
def __init__(self, color, weight, speed=0, wheel_count=4):
self.color = color
self.weight = weight
self.speed = speed
self.wheel_count = wheel_count
def change_speed(self, x):
"""
修改车速
:param x: 表示要修改的车速值。如果是正数,表示加速,负数表示减速,0表示停车
"""
if x == 0: # 如果传递的参数是0,表示要停车
self.speed = 0
return
self.speed += x
if self.speed <= 0 and x < 0:
self.speed = 0
return # return 后面可以什么数据都不加,表示函数结束
class CarAuto(Auto):
def __init__(self, color, weight, ac, navigator, speed=0, wheel_count=4):
super(CarAuto, self).__init__(color, weight, speed, wheel_count)
self.ac = ac
self.navigator = navigator
car = CarAuto('白色', 1.6, '美的', 'iOS', 10, 5)
3
方法一
# 定义一个点类 Pointer
# 属性是横向坐标 x 与纵向坐标 y
# 定义一个圆类 Circle
# 属性有圆心点 cp 与半径radius
# 方法有:
# 1.求圆的面积
# 2.求圆的周长
# 3.求指定点与圆的关系
import math
class Pointer(object):
def __init__(self, x, y):
self.x = x
self.y = y
class Circle(object):
def __init__(self, cp, radius):
self.cp = cp
self.radius = radius
def get_area(self):
return self.radius ** 2 * math.pi
def get_length(self):
return self.radius * 2 * math.pi
def relationship(self, point):
"""
求一个点和圆的关系。有三种关系:在圆内,在圆外,在圆上
:param point:需要判断的点
"""
distance = (abs(point.x) - abs(self.cp.x)) ** 2 + (abs(point.y) - abs(self.cp.y)) ** 2
if distance > self.radius ** 2:
print('在圆外')
elif distance < self.radius ** 2:
print('在圆内')
else:
print('在圆上')
p = Pointer(3, 4) # 创建了一个Pointer对象p
c = Circle(p, 5) # 创建好的Pointer对象p传递给了Circle对象c
print(c.get_area())
print(c.get_length())
p1 = Pointer(10, 10)
c.relationship(p1)
p2 = Pointer(2,2)
c.relationship(p2)
p3 = Pointer(0, 0)
c.relationship(p3)
方法二,使用子类重写父类
import math
class Pointer(object):
def __init__(self, x, y):
self.x = x
self.y = y
class Circle(Pointer):
def __init__(self, x, y, radius):
super(Circle, self).__init__(x, y)
self.radius = radius
def get_area(self):
return self.radius ** 2 * math.pi
def get_length(self):
return self.radius * 2 * math.pi
def relationship(self, point):
distance = (abs(point.x) - abs(self.x)) ** 2 + (abs(point.y) - abs(self.y)) ** 2
if distance > self.radius ** 2:
print('在圆外')
elif distance < self.radius ** 2:
print('在圆内')
else:
print('在圆上')
c = Circle(3, 4, 5)
p = Pointer(10, 10)
c.relationship(p)
4
打印列表
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
def __repr__(self):
return '姓名:{},年龄:{}'.format(self.name, self.age)
p1 = Person('张三', 18)
p2 = Person('李四', 20)
# print(p1) # 调用 __str__ 或者 __repr__ 方法
persons = [p1, p2]
# 直接打印列表,会调用列表里元素的 __repr__ 方法
print(persons) # 直接打印一个列表,会把列表里的每一个对象的内存地址打印出来
方法一
# 宠物店类 PetShop
# 属性:店名,店中的宠物[使用列表存储宠物]
# 方法:展示所有宠物的信息
# 宠物狗类 PetDog
# 属性:昵称,性别,年龄,品种
# 方法:叫,拆家,吃饭
# 宠物猫类 PetCat
# 属性:昵称,性别,年龄,品种,眼睛的颜色
# 方法:叫,撒娇,吃饭
# 注意:狗的叫声是汪汪,猫的叫声是喵喵,狗吃的是骨头,猫吃的是鱼
class PetShop(object):
def __init__(self, shop_name, pet_list=None):
self.shop_name = shop_name
if pet_list is None:
pet_list = []
self.pet_list = pet_list
# def __init__(self, shop_name):
# self.shop_name = shop_name
# self.pet_list = []
def show_pets(self):
if len(self.pet_list) == 0:
print('本店还没有宠物')
return
print('{}店有{}个宠物,它们是:'.format(self.shop_name, len(self.pet_list)))
for pet in self.pet_list:
# print('{}:性别:{},品种:{},年龄:{}岁'.format(pet.name, pet.gender, pet.breed, pet.age))
print(pet)
class Pet(object):
def __init__(self, name, gender, age, breed):
self.name = name
self.gender = gender
self.age = age
self.breed = breed
def bark(self):
print(self.name + '正在叫')
def eat(self):
print(self.name + '正在吃东西')
def __str__(self):
return '姓名:{},性别:{},品种:{},年龄:{}'.format(self.name, self.gender, self.age, self.breed)
class PetDog(Pet):
def bark(self):
print(self.name + '正在汪汪汪')
def build_home(self):
print(self.name + '正在拆家')
def eat(self):
print(self.name + '正在啃骨头')
class PetCat(Pet):
def __init__(self, name, gender, age, breed, eyes_color):
super(PetCat, self).__init__(name, gender, age, breed)
self.eyes_color = eyes_color
def bark(self):
print(self.name + '正在喵喵喵')
def sajiao(self):
print(self.name + '正在撒娇')
def eat(self):
print(self.name + '正在吃鱼')
def __str__(self):
x = super(PetCat, self).__str__()
x += ',眼睛颜色:{}'.format(self.eyes_color)
return x
dog1 = PetDog('大黄', 'female', 3, '哈士奇')
dog2 = PetDog('二黄', 'male', 2, '萨摩耶')
cat1 = PetCat('tom', 'male', 2, '英短', 'blue')
cat2 = PetCat('包子', 'female', 3, '加菲猫', 'black')
ps = PetShop('萌宠', [dog1, dog2, cat1, cat2])
ps.show_pets()
方法二,打印列表
class PetShop(object):
def __init__(self, shop_name, pet_list=None):
self.shop_name = shop_name
if pet_list is None:
pet_list = []
self.pet_list = pet_list
def show_pets(self):
if len(self.pet_list) == 0:
print('本店还没有宠物')
return
print('{}店有{}个宠物,它们是:{}'.format(self.shop_name, len(self.pet_list), self.pet_list))
class Pet(object):
def __init__(self, name, gender, age, breed):
self.name = name
self.gender = gender
self.age = age
self.breed = breed
def bark(self):
print(self.name + '正在叫')
def eat(self):
print(self.name + '正在吃东西')
def __repr__(self):
return '姓名:{},性别:{},品种:{},年龄:{}岁'.format(self.name, self.gender, self.age, self.breed)
class PetDog(Pet):
def bark(self):
print(self.name + '正在汪汪汪')
def build_home(self):
print(self.name + '正在拆家')
def eat(self):
print(self.name + '正在啃骨头')
class PetCat(Pet):
def __init__(self, name, gender, age, breed, eyes_color):
super(PetCat, self).__init__(name, gender, age, breed)
self.eyes_color = eyes_color
def bark(self):
print(self.name + '正在喵喵喵')
def sajiao(self):
print(self.name + '正在撒娇')
def eat(self):
print(self.name + '正在吃鱼')
def __repr__(self):
x = super(PetCat, self).__repr__()
x += ',眼睛颜色:{}'.format(self.eyes_color)
return x
dog1 = PetDog('大黄', 'female', '哈士奇', 3)
dog2 = PetDog('二黄', 'male','萨摩耶', 2)
cat1 = PetCat('tom', 'male','英短', 2, 'blue')
cat2 = PetCat('包子', 'female','加菲猫', 3, 'black')
ps = PetShop('萌宠', [dog1, dog2, cat1, cat2])
ps.show_pets()
5
# 学生类 Student
# 属性:学号,姓名,年龄,性别,成绩
# 班级类 Grade
# 属性:班级名称,班级中的学生[使用列表存储学生]
# 方法:
# 1.查看该班级中的所有学生的信息
# 2.查看指定学号的学生信息
# 3.查看班级中成绩不及格的学生信息
# 4.将班级中的学生按照成绩降序排序
class Student(object):
def __init__(self, number, name, age, gender, score):
self.number = number
self.name = name
self.age = age
self.gender = gender
self.score = score
def __str__(self):
return '学号:{},姓名:{},年龄:{},性别:{},成绩:{}'.format(self.number, self.name, self.age, self.gender,
self.score)
class Grade(object):
def __init__(self, name, students):
self.name = name
self.students = students
def show_all(self):
for k in self.students:
print(k)
def get_student_by_number(self, n):
for i in self.students:
if i.number == n:
return i
else:
return '用户未找到'
def failed_students(self):
result = filter(lambda stu: stu.score < 60, self.students)
for j in result:
print(j)
def order_student(self):
# self.students.sort(key=lambda s: s.score, reverse=True) # 直接修改 self.students 属性
return sorted(self.students, key=lambda s: s.score, reverse=True)
# 如果数字以0开头,在Python2里表示八进制
s1 = Student(1, 'zhangsan', 18, 'male', 80)
s2 = Student(5, '李四', 19, 'male', 50)
s3 = Student(10, 'tony', 20, 'male', 70)
s4 = Student(7, 'jack', 18, 'female', 90)
s5 = Student(4, 'henry', 19, 'female', 56)
g = Grade('中二班', [s1, s2, s3, s4, s5])
# g.show_all()
# print(g.get_student_by_number(1))
# g.failed_students()
x = g.order_student()
for student in x:
print(student)