面向对象程序设计

一、类和对象

  即类别、种类,是面向对象设计最重要的概念,对象是特征与技能的结合体,就是一系列对象相似特征和技能的结合体,针对同样的事物,站在不同的角度得到的类是不同的。需要注意的是:

  • 站的角度不同,定义出的类是截然不同的;
  • 现实中的类并不完全等于程序中的类,比如现实中的公司类,在程序中有时需要拆分成部门类,业务类等;
  • 有时为了编程需求,程序中也可能会定义现实中不存在的类,比如策略类,现实中并不存在,但是在程序中却是一个很常见的类。

1、对象和类的先后关系

  在现实世界中一定是先有对象后有类;但在程序中,却是恰恰相反,一定是先定义类,后调用类来产生对象

# 先定义类
class LuffyStudent:
    school = 'luffycity'

    def learn(self):
        print('is learning')

    def eat(self):
        print('is eating')

    def sleep(self):
        print('is sleeping')


# 后产生对象
stu1 = LuffyStudent()  # 不是执行类体,而是实例化
stu2 = LuffyStudent()
stu3 = LuffyStudent()
print(stu1)  # <__main__.LuffyStudent object at 0x10401ae80>

 注意:

  • 类中可以有任意Python代码,这些代码的类定义阶段便会执行,因而会产生新的名称空间,用来存放类的变量名和函数名,可以通过__dict__查看。
  • 类中定义的名字,都是类的属性,点是访问属性的语法。
  • 对于经典类来说我们可以通过该字典操作类名称空间的名字,但新式类有限制。

2、__dict__方法

   __dict__是一个字典,键为属性名,值为属性值。类的__dict__存储所有实例共享的变量和函数(类属性,方法等),类的__dict__并不包含其父类的属性。

class LuffyStudent:
    school = 'luffycity'

    def learn(self):
        print('is learning')

    def eat(self):
        print('is sleeping')


print(LuffyStudent.__dict__)
print(LuffyStudent.__dict__['school'])
print(LuffyStudent.__dict__['learn'])
"""
{'__module__': '__main__', 'school': 'luffycity', 'learn': <function LuffyStudent.learn at 0x101eb3620>, 'eat': <function LuffyStudent.eat at 0x101f211e0>, '__dict__': <attribute '__dict__' of 'LuffyStudent' objects>, '__weakref__': <attribute '__weakref__' of 'LuffyStudent' objects>, '__doc__': None}
luffycity
<function LuffyStudent.learn at 0x101eb3620>
"""

需要注意__dict__与dir()函数的区别:  

  1、dir()是Python提供的一个API函数,dir()函数会自动寻找一个对象的所有属性(包括从父类继承的属性),包括__dict__中的属性。

  2、__dict__是dir()的子集,一个实例的__dict__属性仅仅是那个实例的实例属性的集合,并不包含该实例的所有有效属性。

3、特殊类属性

__name__:查看类的名字(这里的类名是字符串类型的)

__dict__:查出一个字典,key为属性名,value为属性值

__doc__:显示注释

dir(类名):查看类,以列表的形式显示出来

__module__:类定义所在的模块

__class__:

isinstance(stu1, LuffyStudent):判断对象是否是类的实例

4、引用类的属性

  在使用类时,可以对类的属性进行增删改查操作。

class LuffyStudent:
    school = 'luffycity'

    def learn(self):
        print('is learning')

    def eat(self):
        print('is sleeping')

# 查看
print(LuffyStudent.school)  # luffystudent.__dict__['school']
print(LuffyStudent.learn)  # Luffystudent.__dict_ ('learn')
"""
luffycity
<function LuffyStudent.learn at 0x101cb3620>
"""

#
LuffyStudent.country = 'China'
print(LuffyStudent.__dict__)
print(LuffyStudent.country)
"""
{'__module__': '__main__', 'school': 'luffycity', 'learn': <function LuffyStudent.learn at 0x101eb3620>, 'eat': <function LuffyStudent.eat at 0x1040211e0>, '__dict__': <attribute '__dict__' of 'LuffyStudent' objects>, '__weakref__': <attribute '__weakref__' of 'LuffyStudent' objects>, '__doc__': None, 'country': 'China'}
China
"""

#
del LuffyStudent.country

#
LuffyStudent.school = 'whu'
print(LuffyStudent.school)
"""
whu
"""

5、实例化

  调用类(实例化),得到程序中的对象。

class LuffyStudent:
    school = 'luffycity'

    def learn(self):
        print('is learning')

    def eat(self):
        print('is eating')

    def sleep(self):
        print('is sleeping')


# 后产生对象
stu1 = LuffyStudent()  # 不是执行类体,而是实例化
stu2 = LuffyStudent()
stu3 = LuffyStudent()
print(stu1)
print(stu2)
print(stu3)
"""
<__main__.LuffyStudent object at 0x10401ae80>
<__main__.LuffyStudent object at 0x10401af28>
<__main__.LuffyStudent object at 0x10401af60>
"""

  实例化完成后,stu1、stu2、stu3都一样了,这三者除了相似的属性外还有各种不同的属性,就需要用到__init__方法了。

6、__init__方法:为对象定制自己独有的特征

   __init__方法是在对象产生之后才会执行,只用来为对象进行初始化操作,可以有任意代码,但一定不能有返回值。

  实例化后会产生对象的名称空间,对象的名称空间也可以用stu1.__dict__查看:{'Name': '百合', 'Sex': '女', 'Age': 12}

class LuffyStudent:
    school = 'luffycity'

    def __init__(self, name, sex, age):  # 实例化时自动调用
        self.Name = name
        self.Sex = sex
        self.Age = age

    def learn(self):
        print('is learning')

    def eat(self):
        print('is sleeping')


# 产生对象
stu1 = LuffyStudent('百合', '', 12)
print(stu1.Name)

"""
加上__init__方法后,实例化的步骤
    1、先产生一个空对象
    2、LuffyStudent.__init__(stu1, '百合', '女', 12)
"""

#
print(stu1.__dict__)
print(stu1.Name)
print(stu1.Sex)
print(stu1.Age)
"""
{'Name': '百合', 'Sex': '女', 'Age': 12}
百合
女
12
"""

#
stu1.Name = '李二丫'
print(stu1.__dict__)
print(stu1.Name)
"""
{'Name': '李二丫', 'Sex': '女', 'Age': 12}
李二丫
"""

# 删除
del stu1.Name
print(stu1.__dict__)
"""
{'Sex': '女', 'Age': 12}
"""

#
stu1.class_name = 'python开发'
print(stu1.__dict__)
"""
{'Sex': '女', 'Age': 12, 'class_name': 'python开发'}
"""

stu2 = LuffyStudent('李三炮', '', 22)
print(stu2.__dict__)
"""
{'Name': '李三炮', 'Sex': '男', 'Age': 22}
"""

  当类有了__init__方法后,实例化时先调用类产生空对象,再调用__init__()方法。

  上述代码中也包含有对象的使用,对对象的增删改查操作。

7、关于self

  self,就是实例本身!你实例化时python解释器会自动把这个实例本身通过self参数传进去。

  self是实例化时自动将对象/实例本身传给__init__的第一个参数,你也可以给它起个别的名字,但是一般正常人都不会这么做,因为改了别人就不认识了。

注意:def  __init__(self):   这句话可以写也可以不写,只要有参数参进来的时候就必须得写

   def  方法名(self):这里的self必须得写

二、属性查找

  类中有两种属性:数据属性和函数属性。

class LuffyStudent:
    school = 'luffycity'

    def __init__(self, name, sex, age):  # 实例化时自动调用
        self.Name = name
        self.Sex = sex
        self.Age = age

    def learn(self):
        print('%s is learning' % self.Name)

    def eat(self):
        print('%s is sleeping' % self.Name)


# 产生对象
stu1 = LuffyStudent('百合', '', 12)
stu2 = LuffyStudent('李三炮', '', 38)
stu3 = LuffyStudent('张铁蛋', '', 48)

# 对象:特征和技能的结合体
# 类:类是一系列对象相似的特征与相似技能的结合体

1、类的数据属性是所有对象共享的

# 类中数据属性:是所有对象共有的(都是一样的)
print(LuffyStudent.school, id(LuffyStudent.school))

print(stu1.school, id(stu1.school))
print(stu2.school, id(stu2.school))
"""内存地址一样
luffycity 4325120112
luffycity 4325120112
luffycity 4325120112
"""

2、类的函数属性是绑定给对象用的,称为绑定到对象的方法。

# 类中函数属性:是绑定给对象的,绑定到不同的对象是不同的绑定方法,对象调用绑定方法时,会把对象本身当做第一个参数传入,传给self
print(LuffyStudent.learn)
LuffyStudent.learn(stu1)
"""
<function LuffyStudent.learn at 0x1040211e0>
百合 is learning
"""

print(stu1.learn)
print(stu2.learn)
print(stu3.learn)
"""绑定方法,每个人的函数内存地址不同
<bound method LuffyStudent.learn of <__main__.LuffyStudent object at 0x10402cc18>>
<bound method LuffyStudent.learn of <__main__.LuffyStudent object at 0x10402cc50>>
<bound method LuffyStudent.learn of <__main__.LuffyStudent object at 0x10402cc88>>
"""

3、属性查找顺序

  优先在对象中查找,对象中没有再在类中查找,类中没有会去父类查找,最后都没有则抛出异常。

# 属性查找,优先对象中查找,对象中没有在类中查找
# stu1.x = 'from stu1'
LuffyStudent.x = 'from Luffycity class'

print(stu1.__dict__)
print(stu1.x)
"""
{'Name': '百合', 'Sex': '女', 'Age': 12}
from Luffycity class
"""

三、绑定方法

  在属性查找那一小节中,定义了类并实例化出了三个对象stu1、stu2、stu3。

class LuffyStudent:
    school = 'luffycity'

    def __init__(self, name, sex, age):  # 实例化时自动调用
        self.Name = name
        self.Sex = sex
        self.Age = age

    def learn(self):
        print('%s is learning' % self.Name)

    def eat(self):
        print('%s is sleeping' % self.Name)


# 产生对象
stu1 = LuffyStudent('百合', '', 12)
stu2 = LuffyStudent('李三炮', '', 38)
stu3 = LuffyStudent('张铁蛋', '', 48)
定义类并实例化对象

  类中定义的函数(没有被任何装饰器装饰的)是类的函数属性,类可以使用,但必须遵循函数的参数规则,有几个参数需要传几个参数。

LuffyStudent.learn(stu1)
LuffyStudent.learn(stu2)
LuffyStudent.learn(stu3)
"""
百合 is learning
李三炮 is learning
张铁蛋 is learning
"""

  类中定义的函数(没有被任何装饰器装饰的),其实主要是给对象使用的,而且是绑定到对象的,虽然所有对象指向的都是相同的功能,但是绑定到不同的对象就是不同的绑定方法

  强调:绑定到对象的方法的特殊之处在于,绑定给谁就由谁来调用,谁来调用,就会将‘谁’本身当做第一个参数传给方法,即自动传值(方法__init__也是一样的道理)

stu1.learn()
stu2.learn()
stu3.learn()
"""
百合 is learning
李三炮 is learning
张铁蛋 is learning
"""

  注意:绑定到对象的方法的这种自动传值的特征,决定了在类中定义的函数都要默认写一个参数self,self可以是任意名字,但是约定俗成地写出self。

四、类即类型

  python中一切皆为对象,且Python3中类与类型是一个概念,类型就是类。

class student:
    school = 'whu'

# python当中一切皆对象,在python3中统一了类和类型的概念
print(list)
print(dict)∂
print(student)
"""
<class 'list'>
<class 'dict'>
<class '__main__.student'>
"""

   以list类型举例,类型list就是类list,对类实例化后,对象调用绑定方法append为对象添加元素。

>>> list
<class 'list'>

#实例化的到3个对象l1,l2,l3
>>> l1=list()
>>> l2=list()
>>> l3=list()

#三个对象都有绑定方法append,是相同的功能,但内存地址不同
>>> l1.append
<built-in method append of list object at 0x10b482b48>
>>> l2.append
<built-in method append of list object at 0x10b482b88>
>>> l3.append
<built-in method append of list object at 0x10b482bc8>

#操作绑定方法l1.append(3),就是在往l1添加3,绝对不会将3添加到l2或l3
>>> l1.append(3)
>>> l1
[3]
>>> l2
[]
>>> l3
[]
#调用类list.append(l3,111)等同于l3.append(111)
>>> list.append(l3,111) #l3.append(111)
>>> l3
[111]

五、面向对象小结

1、定义及调用的固定格式

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   #查看对象的属性,直接用 对象名.属性名 即可
对象名.方法名()     #调用类中的方法,直接用 对象名.方法名() 即可

2、运用面向对象好处

(1)将数据和专门操作该数据的功能整合到一起

  避免定义一大堆全局变量。

class MySQLHandler:
    def __init__(self,host,port,db,charset='utf8'):
        self.host=host
        self.port=port
        self.db=db
        self.charset=charset
        self.conn=connect(self.host,self.port,self.db,self.charset)
    def exc1(self,sql):
        return self.conn.execute(sql)

    def exc2(self,sql):
        return self.conn.call_proc(sql)


obj=MySQLHandler('127.0.0.1',3306,'db1')
obj.exc1('select * from tb1;')
obj.exc2('存储过程的名字')

(2)可扩展性高

  定义类并产生多个对象

class Chinese:
    def __init__(self,name,age,sex):
        self.name=name
        self.age=age
        self.sex=sex


p1=Chinese('egon',18,'male')
p2=Chinese('alex',38,'female')
p3=Chinese('wpq',48,'female')

  如果新增一个类属性,会立刻反映给所有对象,而对象却无需修改。

class Chinese:
    country='China'
    def __init__(self,name,age,sex):
        self.name=name
        self.age=age
        self.sex=sex
    def tell_info(self):
        info='''
        国籍:%s
        姓名:%s
        年龄:%s
        性别:%s
        ''' %(self.country,self.name,self.age,self.sex)
        print(info)


p1=Chinese('egon',18,'male')
p2=Chinese('alex',38,'female')
p3=Chinese('wpq',48,'female')

print(p1.country)
p1.tell_info()

3、面向对象练习题 

练习1:编写一个学生类,产生一堆学生对象。

要求:有一个计数器(属性),统计总共实例了多少个对象

class Student:   # 类名头字母大写
    school = 'whu'
    count = 0

    def __init__(self, name, age, sex):  # 为对象定制对象自己独有的特征
        self.name = name
        self.age = age
        self.sex = sex
        # self.count += 1  # 每个对象都是1,无法实现累加,student类的count一直都是0
        Student.count += 1

    def learn(self):
        print('%s is learning' % self.name)

    def eat(self):
        print('%s is eating very happy!' % self.name)


stu1 = Student('alex', 'male', 38)   # 实例化一次就触发一次__init__
stu2 = Student('jinxing', 'female', 48)
stu3 = Student('egon', 'male', 18)


print(Student.count)
print(stu1.count)
print(stu2.count)
print(stu3.count)
print(stu1.__dict__)
print(stu3.__dict__)
"""
3
3
3
3
{'name': 'alex', 'age': 'male', 'sex': 38}
{'name': 'egon', 'age': 'male', 'sex': 18}
"""
学生类具备计算器

练习2:模仿王者荣耀定义两个英雄类, (10分钟)

要求:1、英雄需要有昵称、攻击力、生命值等属性;

   2、实例化出两个英雄对象;

   3、英雄之间可以互殴,被殴打的一方掉血,血量小于0则判定为死亡。

class Garen:
    camp = 'Demacia'

    def __init__(self, nickname, life_value, aggresivity):
        self.nickname = nickname
        self.life_value = life_value
        self.aggresivity = aggresivity

    def attack(self, enemy):
        enemy.life_value -= self.aggresivity


class Riven:
    camp = 'Noxus'

    def __init__(self, nickname, life_value, aggresivity):
        self.nickname = nickname
        self.life_value = life_value
        self.aggresivity = aggresivity

    def attack(self, enemy):
        enemy.life_value -= self.aggresivity


g1 = Garen('草丛伦', 100, 30)

r1 = Riven('可爱的瑞雯', 80, 50)

print(r1.life_value)
g1.attack(r1)  # 攻击了一刀
r1.attack(g1)
print(r1.life_value)
print(g1.life_value)
英雄角色类互相交互

4、判断是函数还是方法?

函数:函数是封装了一些独立的功能,可以直接调用,python内置了许多函数,同时可以自建函数来使用。

方法:方法和函数类似,同样封装了独立的功能,但是方法是需要通过对象来调用的,表示针对这个对象要做的操作,使用时采用点方法。

posted @ 2018-04-21 20:59  休耕  阅读(445)  评论(0编辑  收藏  举报