引子

小游戏:人狗大战

角色:人和狗

角色属性:姓名,血量,战斗力和性别(种类)

技能:打/咬

用函数实现人打狗和狗咬人的情形

def Dog(name,blood,aggr,kind):
    dog = {
        'name':name,
        'blood':blood,
        'aggr':aggr,
        'kind':kind
    }
    def bite(person):
        person['blood'] -= dog['aggr']
        print('%s被咬了,掉了%s的血'%(person['name'],dog['aggr']))
    dog['bite'] = bite
    return dog
def Person(name,blood,aggr,sex):
    person = {
        'name':name,
        'blood':blood,
        'aggr':aggr,
        'sex':sex
    }
    def attack(dog):
        dog['blood'] -= person['aggr']
        print('%s被打了,掉了%s的血'%(dog['name'],person['aggr']))
    person['attack'] = attack
    return person
person = Person('武大',100,5,'nan')
dog = Dog('大黄',100,2,'jin')
dog['bite'](person)
person['attack'](dog)
print(person)
print(dog)
>>>
武大被咬了,掉了2的血
大黄被打了,掉了5的血
{'name': '武大', 'blood': 98, 'aggr': 5, 'sex': 'nan', 'attack': <function Person.<locals>.attack at 0x000002729A1CFB70>}
{'name': '大黄', 'blood': 95, 'aggr': 2, 'kind': 'jin', 'bite': <function Dog.<locals>.bite at 0x000002729A1CFBF8>}

这个简单的例子使用的编程思想就是简单的面向对象编程,

面向过程 VS 面向对象 

面向过程的程序设计的核心是过程(流水线式思维),过程即解决问题的步骤,面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西。

优点是:极大的降低了写程序的复杂度,只需要顺着要执行的步骤,堆叠代码即可。

缺点是:一套流水线或者流程就是用来解决一个问题,代码牵一发而动全身。

应用场景:一旦完成基本很少改变的场景,著名的例子有Linux內核,git,以及Apache HTTP Server等。

 面向对象的程序设计的核心是对象(上帝式思维),要理解对象为何物,必须把自己当成上帝,上帝眼里世间存在的万物皆为对象,不存在的也可以创造出来。面向对象的程序设计好比如来设计西游记,如来要解决的问题是把经书传给东土大唐,如来想了想解决这个问题需要四个人:唐僧,沙和尚,猪八戒,孙悟空,每个人都有各自的特征和技能(这就是对象的概念,特征和技能分别对应对象的属性和方法),然而这并不好玩,于是如来又安排了一群妖魔鬼怪,为了防止师徒四人在取经路上被搞死,又安排了一群神仙保驾护航,这些都是对象。然后取经开始,师徒四人与妖魔鬼怪神仙互相缠斗着直到最后取得真经。如来根本不会管师徒四人按照什么流程去取。

面向对象的程序设计的

优点是:解决了程序的扩展性。对某一个对象单独修改,会立刻反映到整个体系中,如对游戏中一个人物参数的特征和技能修改都很容易。

缺点:可控性差,无法向面向过程的程序设计流水线式的可以很精准的预测问题的处理流程与结果,面向对象的程序一旦开始就由对象之间的交互解决问题即便是上帝也无法预测最终结果。于是我们经常看到一个游戏人某一参数的修改极有可能导致阴霸的技能出现,一刀砍死3个人,这个游戏就失去平衡。

应用场景:需求经常变化的软件,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方。

在python 中面向对象的程序设计并不是全部。

面向对象编程可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。

相关名词:类、对象、实例、实例化

类:具有相同特征的一类事物(人、狗、老虎)

对象/实例:具体的某一个事物(隔壁阿花、楼下旺财)

实例化:类——>对象的过程

初识类和对象

python中一切皆为对象,类型的本质就是类。在python中,用变量表示特征,用函数表示技能,因而具有相同特征和技能的一类事物就是‘类’,对象是则是这一类事物中具体的一个。

类:

def functionName(args):
     '函数文档字符串'
      函数体
'''
class 类名:
    '类的文档字符串'
    类体
'''

#我们创建一个类
class Data:
    pass

声明类
class Person:   #定义一个人类
    role = 'person'  #人的角色属性都是人
    def walk(self):  #人都可以走路,也就是有一个走路方法,也叫动态属性
        print("person is walking...")

类有两种作用:属性引用和实例化

属性引用(类名.属性)

class Person:   #定义一个人类
    role = 'person'  #人的角色属性都是人
    def walk(self):  #人都可以走路,也就是有一个走路方法
        print("person is walking...")


print(Person.role)  #查看人的role属性
print(Person.walk)  #引用人的走路方法,注意,这里不是在调用

实例化:类名加括号就是实例化,会自动触发__init__函数的运行,可以用它来为每个实例定制自己的特征

class Person:   #定义一个人类
    role = 'person'  #人的角色属性都是人
    def __init__(self,name):
        self.name = name  # 每一个角色都有自己的昵称;
        
    def walk(self):  #人都可以走路,也就是有一个走路方法
        print("person is walking...")


print(Person.role)  #查看人的role属性
print(Person.walk)  #引用人的走路方法,注意,这里不是在调用

 

实例化的过程就是类——>对象的过程

原本我们只有一个Person类,在这个过程中,产生了一个武大对象,有自己具体的名字、攻击力和生命值。

语法:对象名 = 类名(参数)

武大 = Person('武大')  #类名()就等于在执行Person.__init__()
#执行完__init__()就会返回一个对象。这个对象类似一个字典,存着属于这个人本身的一些属性和方法。
#你可以偷偷的理解:武大 = {'name':'武大','walk':walk}

查看属性和调用方法:

print(武大.name)     #查看属性直接 对象名.属性名
print(武大.walk())   #调用方法,对象名.方法名()

关于self:
self:在实例化时自动将对象/实例本身传给__init__的第一个参数,可以是其他名字,但默认都是这个,要不然其他人会不认识的。

类属性的补充:

一:我们定义的类的属性到底存到哪里了?有两种方式查看
dir(类名):查出的是一个名字列表
类名.__dict__:查出的是一个字典,key为属性名,value为属性值

二:特殊的类属性
类名.__name__# 类的名字(字符串)
类名.__doc__# 类的文档字符串
类名.__base__# 类的第一个父类(继承相关知识)
类名.__bases__# 类所有父类构成的元组(继承相关知识)
类名.__dict__# 类的字典属性
类名.__module__# 类定义所在的模块
类名.__class__# 实例对应的类(仅新式类中)

类属性的补充

 对象

对象是关于类而实际存在的一个例子,即实例。

对象/实例只有一种作用:属性引用

理解面向对象:

def Person(*args,**kwargs):
    self = {}
    def attack(self,dog):
        dog['life_value'] -= self['aggressivity']

    def __init__(name,aggressivity,life_value):
        self['name'] = name
        self['aggressivity'] = aggressivity
        self['life_value'] = life_value
        self['attack'] = attack

    __init__(*args,**kwargs)
    return self

egg = Person('武大',78,10)
print(武大['name'])

帮你了解面向对象

面向对象小结——定义及调用的固定模式

class 类名:
    def __init__(self,参数1,参数2):
        self.对象的属性1 = 参数1
        self.对象的属性2 = 参数2

    def 方法名(self):pass

    def 方法名2(self):pass

对象名 = 类名(1,2)  #对象就是实例,代表一个具体的东西
                  #类名() : 类名+括号就是实例化一个类,相当于调用了__init__方法
                  #括号里传参数,参数不需要传self,其他与init中的形参一一对应
                  #结果返回一个对象
对象名.对象的属性1   #查看对象的属性,直接用 对象名.属性名 即可
对象名.方法名()     #调用类中的方法,直接用 对象名.方法名() 即可

小结

练习

练习:在终端输出如下信息

小明,10岁,男,上山去砍柴
小明,10岁,男,开车去东北
小明,10岁,男,最爱大保健
老李,90岁,男,上山去砍柴
老李,90岁,男,开车去东北
老李,90岁,男,最爱大保健
老张…

练一练
class Person:
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex
    def shangshan(self):
        print('%s,%s岁,%s,上山去砍柴'%(self.name,self.age,self.sex))

    def drive(self):
        print('%s,%s岁,%s,开车去东北' % (self.name,self.age,self.sex))

    def favor(self):
        print('%s,%s岁,%s,最爱大保健' % (self.name,self.age,self.sex))

小明 = Person('小明',10,'男')
小明.shangshan()
小明.drive()
小明.favor()
老李 = Person('老李',90,'男')
老李.shangshan()
老李.drive()
老李.favor()

计算已知半径圆的周长和面积

from math import pi
class Circular:
    def __init__(self,r):
        self.r = r
    def perimeter(self):
        return self.r*2*pi
    def acreage(self):
        return self.r**2*pi
c1 = Circular(4)
print(c1.perimeter())
print(c1.acreage())

 计算正方形的周长和面积

class Square:
    def __init__(self,l):
        self.l = l
    def perimeter(self):
        return 4*self.l
    def acreage(self):
        return self.l**2
square = Square(5)
print(square.perimeter())
print(square.acreage())

 对象之间的交互

人狗大战:

import time
class Person:
    def __init__(self,name,age,sex,country,hp,aggr):
        self.name = name
        self.age = age
        self.sex = sex
        self.country = country
        self.hp = hp
        self.aggr = aggr
    def Hit(self,dog):
        dog.hp -= self.aggr
        if dog.hp <= 0 :
            print('%s打了%s,造成%s点伤害,已扑街。。。'%(self.name,dog.name,self.aggr))
        else:
            print('%s打了%s,造成%s点伤害,剩余%s点生命值'%(self.name,dog.name,self.aggr,dog.hp))
class Dog:
    def __init__(self,name,hp,aggr,kind):
        self.name = name
        self.hp = hp
        self.aggr = aggr
        self.kind = kind
    def bite(self,person):
        person.hp -= self.aggr
        if person.hp <= 0:
            print('%s咬了%s,造成%s点伤害,已扑街。。。'%(self.name,person.name,self.aggr))
        else:
            print('%s咬了%s,造成%s点伤害,剩余%s点生命值'%(self.name,person.name,self.aggr,person.hp))
小明 = Person('小明',18,'男','中国',1000,50)
啸天犬 = Dog('啸天',600,80,'teddy')
print('>>>>>欢迎来到人狗大战世界<<<<<')
while 1:
    choice = input('》》》')
    if choice.upper() == 'Q':
        break
    elif choice.upper() == 'W' :
        小明.Hit(啸天犬)
    elif choice.upper() == 'E':
        啸天犬.bite(小明)
    time.sleep(1)

人狗大战

 类命名空间与对象、实例的命名空间

创建一个类就会创建一个类的名称空间,用来存储类中定义的所有名字,这些名字称为类的属性。

而类有两种属性:静态属性和动态属性

  • 静态属性就是直接在类中定义的变量
  • 动态属性就是定义在类中的方法

其中类的数据属性是共享给所有对象的

而类的动态属性是绑定到所有对象的

创建一个对象/实例就会创建一个对象/实例的名称空间,存放对象/实例的名字,称为对象/实例的属性

只能在对象或实例中的名称空间里找动态属性,找不到就到类中的名称空间里找,找不到就报错,不会在类外面找。

面向对象的组合用法

组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合

例子:人狗大战

class Dog:
    def __init__(self,name,aggr,hp,kind):
        self.name = name
        self.aggr = aggr
        self.hp = hp
        self.kind = kind

    def bite(self,person):
        person.hp -= self.aggr

class Person:
    def __init__(self,name,aggr,hp,sex):
        self.name = name
        self.aggr = aggr
        self.hp = hp
        self.sex = sex
        self.money = 0

    def attack(self,dog):
        dog.hp -= self.aggr

    def get_weapon(self,weapon):
        if self.money >= weapon.price:
            self.money -= weapon.price
            self.weapon = weapon
            self.aggr += weapon.aggr
        else:
            print("余额不足,请先充值")

class Weapon:
    def __init__(self,name,aggr,njd,price):
        self.name = name
        self.aggr = aggr
        self.njd = njd
        self.price = price

    def hand18(self,person):
        if self.njd > 0:
            person.hp -= self.aggr * 2
            self.njd -= 1

武大 = Person('武大',20,300,'不详')
哮天犬 = Dog('哮天犬',100,500,'teddy')
w = Weapon('打狗棒',100,3,998)
# alex装备打狗棒
武大.money += 1000
武大.get_weapon(w)
print(武大.weapon)
print(武大.aggr)
武大.attack(哮天犬)
print(哮天犬.hp)
武大.weapon.hand18(哮天犬)
print(哮天犬.hp)

组合的例子--人狗大战

圆环是由两个圆组成的,圆环的面积是外面圆的面积减去内部圆的面积。圆环的周长是内部圆的周长加上外部圆的周长。
这个时候,我们就首先实现一个圆形类,计算一个圆的周长和面积。然后在"环形类"中组合圆形的实例作为自己的属性来用

from math import pi
class Circle:
    def __init__(self,r):
        self.r = r
    def area(self):
        return self.r**2 * pi
    def perimeter(self):
        return 2*pi*self.r
class Ring:
    def __init__(self,outside_r,inside_r):
        self.outside_c = Circle(outside_r)
        self.inside_c = Circle(inside_r)
    def area(self):
        return self.outside_c.area() - self.inside_c.area()
    def perimeter(self):
        return self.outside_c.perimeter() + self.inside_c.perimeter()
r = Ring(5,2)
print(r.area())

class Teacher:
    def __init__(self,name,birthday):
        self.name = name
        self.birth = birthday
class Birthday:
    def __init__(self,day):
        self.day = day
b = Birthday(123456789)
t = Teacher('123',b)
print(t.birth.day)

计算圆环的周长和面积

用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如教授有生日

class BirthDate:
    def __init__(self,year,month,day):
        self.year=year
        self.month=month
        self.day=day

class Couse:
    def __init__(self,name,price,period):
        self.name=name
        self.price=price
        self.period=period

class Teacher:
    def __init__(self,name,gender,birth,course):
        self.name=name 
        self.gender=gender
        self.birth=birth
        self.course=course
    def teach(self): 
        print('teaching')

p1=Teacher('egon','male', 
            BirthDate('1995','1','27'), 
            Couse('python','28000','4 months')
           ) 

print(p1.birth.year,p1.birth.month,p1.birth.day) 

print(p1.course.name,p1.course.price,p1.course.period)
''' 
运行结果: 
27 
python 28000 4 months 
'''

教授的生日

当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好