大鹏

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 :: 管理 ::

 第十一章 类和对象

本章内容如下:

    1、对象=属性+方法
    2、面向对象编程
      2.1 self是什么
      2.2 你听说过python的魔法方法吗
      2.3 公有和私有
    3、继承
      3.1 调用未绑定的父类方法
      3.2 使用super函数
    4、多重继承
    5、组合
    6、类、类对象和实例对象
    7、到底什么是绑定
    8、一些相关的BIF
    9、作业
    10、扩展阅读

 

++++++++++++++++++++++++++++++++

1、对象=属性+方法

 

一个对象的特征称为“属性”,一个对象的行为称为“方法”。通过下面的例子来介绍对象:

class Turtle:
    # Python 中的类名约定以大写字母开头

    # 特征的描述我们称为属性,在代码层面来看其实就是变量

    color = 'green'
    weight = 10
    legs = 4
    shell = True
    mouth = '大嘴'

    # 方法实际就是函数,通过调用这些函数来完成某些工作
    def climb(self):
        print("我正在很努力的向前爬......")

    def run(self):
        print("我正在飞快的向前跑......")

    def bite(self):
        print("咬死你咬死你!!")

    def eat(self):
        print("有得吃,真满足^_^")

    def sleep(self):
        print("困了,睡了,晚安,Zzzz")


"""
1、类:以上代码定义了对象的特征(属性)和行为(方法),但还不是一个完整的对象,将定义的这些称为类(Class)。
2、实例对象(Instance Objects):需要使用类来创建一个真正的对象,这个对象就叫作这个类的一个实例(Instance)。 如:Turtle()
3、类的实例化:创建一个对象,也叫作类的实例化,如:tt = Turtle()
"""


#类的实例化
tt = Turtle()
""""
注意:
1、类名后面跟着笑括号,与函数的调用一样的
2、类名约定用大写字母开头,函数用小写字母开头
3、赋值操作不是必需的,但如果没有把创建好的实例对象赋值给一个变量,那这个对象就没法使用,
因为没有任何引用指向这个实例,最终会被python的垃圾收集机制自动回收
"""

"""
1、要调用对象里的方法,使用点操作符(.)即可
2、调用对象里的属性,与方法类似
"""

#调用方法
tt.climb() #我正在很努力的向前爬...... tt.bite() #咬死你咬死你!! tt.sleep() #困了,睡了,晚安,Zzzz

#调用属性
print(tt.color) # green print(tt.weight) # 10 print(tt.legs) # 4

 

2、面向对象编程

 

2.1 self是什么

 self相当于C++的this指针,告诉python所调用的方法和属性,是属于哪一个实例化对象的

"""
如果把类比作是图纸,那么类的实例化的对象才是真正可以住的房子。
self就相当于每个房子的门牌号,告诉python这个房子是那个实例化对象的

由同一个类可以生成无数个对象,当一个对象的方法被调用的时候,
对象会将自身的引用作为第一个参数传给该方法,
那么python就知道需要操作那个对象的方法了。
"""
class Ball:
    def setName(self,name):
        self.name = name

    def kick(self):
        print("我叫%s,噢~谁踢我?!"%self.name)

a = Ball()    #实例化一个对象a ,
a.setName("飞火流星")  #在调用a的方法时,self会告诉python调用的是a的方法

b = Ball()
b.setName('团对之星')

c = Ball()
c.setName("土豆")

a.kick()         #我叫飞火流星,噢~谁踢我?!
b.kick()         #我叫团对之星,噢~谁踢我?!
c.kick()         #我叫土豆,噢~谁踢我?!


"""
如果没有self指名,调用的方法是那个实例化对象的,就会乱了套
"""

 

2.2 你听说过python的魔法方法吗

 构造函数__init__

"""
#python的这些具有魔力的方法,总是被双下化线所包围。

__init__() 方法称为构造方法,__init__()方法的魔力体现在只要实例化一个对象,这个方法就会在对象被创建时自动调用。
实例化对象时是可以传入参数的,这些参数会自动传入__init__()方法中,可以通过重写这个方法来自定义对象的初始化操作。
"""


class Potato:
    def __init__(self,name):  #初始化时,传入参数name
        self.name = name
    def kick(self):
        print("我叫%s,噢~谁踢我?!"%self.name)



p = Potato('土豆')
p.kick()   # 我叫土豆,噢~谁踢我?!

 

2.3 公有和私有

 公有

#公有
#默认上对象的属性和方法都是公开的,可以直接通过点操作符(.)进行方法

class Person:
    name = "小甲鱼"


p = Person()
print(p.name)      #属性调用不加括号,方法的调用要加括号
# 小甲鱼

私有

#定义私有变量只需要在变量名或函数名前加上“__”两个下划线,那么这个函数或者变量就会变成私有的了
class Person:
    __name = "小甲鱼"   # 变量name 变为私有的了


p = Person()
print(p.__name)      #报错,不能直接访问
# AttributeError: 'Person' object has no attribute '__name'
#私有函数
class __Person:   #函数变成私有的了
    name = "小甲鱼"


p = __Person()
print(p.name)      #运行正常

这样在外部将变量名“隐藏”起来了,理论上如果要访问,就需要从内部进行

class Person:
    def __init__(self,name):
        self.__name = name
    def getName(self):
        return self.__name

p = Person('小甲鱼')

#p.__name    # 不能直接访问
#AttributeError: 'Person' object has no attribute '__name'

print(p.getName())   # 可以间接访问
#小甲鱼

可以使用"_类名__变量名"即可访问双下横线开头的私有变量

#_类名__变量名
p._Person__name

'''
Python目前的私有机制其实是伪私有的,Python的类是没有权限控制的,所有变量都是可以被外部调用的
'''

 

3、继承

 继续的定义

"""
语法:
class 类名(被继承的类):
    ...

被继承的类称为基类,父类或超类;继承者称为子类,一个子类可以继承它的父类的任何属性和方法
"""

class Parent:
    def hello(self):
        print("正在调用父类的方法...")

class Child(Parent):
    pass


p = Parent()
p.hello()   # 正在调用父类的方法...

c = Child()
c.hello()   #正在调用父类的方法...

 

子类定义与父类同名的方法或属性,则会自动覆盖父类对应的方法或属性

"""
注意:
如果子类定义与父类同名的方法或属性,则会自动覆盖父类对应的方法或属性
"""

class Parent:
    def hello(self):
        print("正在调用父类的方法...")

class Child(Parent):
    def hello(self):      #与父类同名的方法
        print("正在调用子类的方法...")



p = Parent()
p.hello()   # 正在调用父类的方法...

c = Child()
c.hello()   #正在调用子类的方法...

 

3.1 调用未绑定的父类方法

 定义一个金鱼(Goldfish)、鲤鱼(Carp)、三文鱼(Salmon),还有鲨鱼(Shark)

import random as r
class Fish:
    def __init__(self):
        self.x = r.randint(0, 10)
        self.y = r.randint(0, 10)
    def move(self):
        # 这里我们主要演示类的继承机制,我们就不考虑检查场景边界和移动方向的问题
        # 这两部分内容在上节课的课后作业里边有,大家可以回头去看一下参考代码即可

        # 假设所有鱼都是一路向西游
        self.x -= 1
        print("我的位置是:", self.x, self.y)

class Goldfish(Fish):
    pass
class Carp(Fish):
    pass
class Salmon(Fish):
    pass
# 上边几个都是食物,食物不需要有个性,所以我们直接继承Fish类的全部属性和方法即可
# 下边我们定义鲨鱼类,这个是吃货,除了继承Fish类的属性和方法,我们还要添加一个吃的方法
class Shark(Fish): def __init__(self): self.hungry = True def eat(self): if self.hungry: print("吃货的梦想就是天天有得吃^_^") self.hungry = False else: print("太撑了,吃不下!")

运行代码

fish = Fish()
fish.move()   #我的位置是: 9 6

goldfish  = Goldfish()
goldfish.move()  #我的位置是: 5 6

shark  = Shark()
shark.eat()   #吃货的梦想就是天天有得吃^_^
shark.move()   # AttributeError: 'Shark' object has no attribute 'x'

会发现 shark.move()会报错,为什么?同样是继承Fish类,为什么金鱼(Goldfish)可以移动,而鲨鱼(Shark)一移动就报错呢?

"""
其实这里的抛出的异常说的很清楚了:Shark对象没有X属性。原因其实是这样的:在Shark类中,重写了魔法方法__init__,但新的__init__
方法里边没有初始化鲨鱼的X坐标和Y坐标,因此调用move()方法就会出错。那么解决这个问题的方案很明显了,应该在鲨鱼类中
重写__init__方法的时候先调用基类Fish的__init__方法。

"""

实现方法一:调用未绑定的父类方法

class Shark(Fish):
    def __init__(self):
        Fish.__init__(self)   #重写父类Fish的__init__
        self.hungry = True

    def eat(self):
        if self.hungry:
            print("吃货的梦想就是天天有得吃^_^")
            self.hungry = False
        else:
            print("太撑了,吃不下!")

 

最终代码及运行结果:

import random as r


class Fish:
    def __init__(self):
        self.x = r.randint(0, 10)
        self.y = r.randint(0, 10)

    def move(self):
        # 这里我们主要演示类的继承机制,我们就不考虑检查场景边界和移动方向的问题
        # 这两部分内容在上节课的课后作业里边有,大家可以回头去看一下参考代码即可

        # 假设所有鱼都是一路向西游
        self.x -= 1
        print("我的位置是:", self.x, self.y)


class Goldfish(Fish):
    pass


class Carp(Fish):
    pass


class Salmon(Fish):
    pass


# 上边几个都是食物,食物不需要有个性,所以我们直接继承Fish类的全部属性和方法即可
# 下边我们定义鲨鱼类,这个是吃货,除了继承Fish类的属性和方法,我们还要添加一个吃的方法

class Shark(Fish):
    def __init__(self):
        Fish.__init__(self)
        self.hungry = True

    def eat(self):
        if self.hungry:
            print("吃货的梦想就是天天有得吃^_^")
            self.hungry = False
        else:
            print("太撑了,吃不下!")




fish = Fish()
fish.move()   #我的位置是: 9 6

goldfish  = Goldfish()
goldfish.move()  #我的位置是: 5 6

shark  = Shark()
shark.eat()   #吃货的梦想就是天天有得吃^_^
shark.move()    #我的位置是: 9 7
完整的代码及结果

 

3.2 使用super函数

 方法二:使用super函数

#super函数能够帮我自动找到基类的方法
class Shark(Fish):
    def __init__(self):
        super().__init__()
        self.hungry = True

    def eat(self):
        if self.hungry:
            print("吃货的梦想就是天天有得吃^_^")
            self.hungry = False
        else:
            print("太撑了,吃不下!")


"""
super函数的"超级"之处在于你不需要明确给出任何基类的名字,它会自动帮您找出所有基类以及对应的方法。

"""

 

4、多重继承

 

"""
多重继承

class 类名(父类1,父类2,父类3,...)
   ...
"""
class Base1:
    def fool(self):
        print('我是fool,我在Base1中...')

class Base2:
    def foo2(self):
        print('我是foo2,我在Base2中...')

class C(Base1,Base2):
    pass

c = C()
c.fool()   #我是fool,我在Base1中...
c.foo2()   #我是foo2,我在Base2中...

[扩展阅读] 多重继承的陷阱:钻石继承(菱形继承)问题

 

5、组合

 同一类型的用继承,不同类型的用组合

#组合,就是把需要的类放进来实例化就可以了
class Turtle:
    def __init__(self, x):
        self.num = x

class Fish:
    def __init__(self, x):
        self.num = x

class Pool:
    def __init__(self, x, y):
        self.turtle = Turtle(x)  #直接把需要的类放进来,实例化就可以了
        self.fish = Fish(y)    #直接把需要的类放进来,实例化就可以了
     
    def print_num(self):
        print("水池里总共有乌龟 %d 只,小鱼 %d 条!" % (self.turtle.num, self.fish.num))

[扩展阅读] Python Mixin 编程机制

 

6、类、类对象和实例对象

 

class C:
    count = 0


a = C()   #类的实例化
b = C()   #类的实例化
c = C()   #类的实例化

c.count +=10  #
print(a.count,b.count,c.count)
# 0 0 10
C.count += 100  
print(a.count,b.count,c.count)
#100 100 10

需要注意的是,如果属性的名字跟方法名相同,属性会覆盖方法。

class C:
    def x(self):
        print("Xman")

c = C()

c.x()

c.x = 1  # 属性和方法名相同,覆盖方法,重写赋值
print(c.x)

c.x()  #方法被覆盖,所以报错
# TypeError: 'int' object is not callable

为了避免一些名字上的冲突,应该遵守一些约定成熟的规矩:

1、类的定义要“少吃多餐”,尽量用继承和组合机制来进行扩展。

2、不同的词性命名,如属性名用名词,方法名用动词,并使用骆驼命名法等

 

7、到底什么是绑定

 python严格要求方法需要由实例才能被调用,这种限制其实就是python所谓的绑定概念。

案例一

class BB:
    def printBB():  #缺失self
        print("no zuo no die")

BB.printBB()

#如果进行实例化,由于没有self指明是那个实例对象的,所以根本无法调用里边的函数
bb = BB()
bb.printBB()
#TypeError: printBB() takes 0 positional arguments but 1 was given
#这是由于绑定机制,会自动的把bb对象作为第一个参数传入,所以才会出现TypeError

 

深入理解

class CC:
    def setXY(self,x,y):
        self.x = x
        self.y = y
    def printXY(self):
        print(self.x,self.y)

dd = CC()
print(dd.__dict__)   # 查看__dict__所拥有的属性
#   {}  一个空字典

print(CC.__dict__)

"""
结果:
{'__module__': '__main__', 'setXY': <function CC.setXY at 0x00000000022E9510>, 'printXY': <function CC.printXY at 0x00000000022E9598>,
 '__dict__': <attribute '__dict__' of 'CC' objects>, '__weakref__': <attribute '__weakref__' of 'CC' objects>, '__doc__': None}

__dict__属性是由一个字典组成,字典中仅有实例对象的属性,不显示类属性和特殊属性,键表示的是属性名,值表示属性相应的数据值
"""

dd.setXY(4,5)  #赋值
print(dd.__dict__)   #这时就有值了
# {'x': 4, 'y': 5}  # 实例对象dd有了两个新的属性,而且这两个属性仅属于实例对象的

print(CC.__dict__)   #没有 {'x': 4, 'y': 5}

"""
为什么会这样?
完全归功于self参数:当实例对象dd去调用setXY方法的时候,它传入 的第一个参数就是dd,那么self.x = 4,self.y=5也就相当于
dd.x= 4,dd.y=5,所以你在实例对象,甚至类对象中都看不到x和y,因为这两个属性是只属于实例对象dd的。
"""

# 接着往下,删除类实例,实例对象dd还能否调用printXY方法
del CC   #删除类实例

print(dd.printXY())   # 4 5

 

8、一些相关的BIF

 1、issubclass(clas,classinfo)

issubclass(class,classinfo)
'''
如果第一个参数(class)是第二个参数(classinfo)的一个子类,则返回True,否则返回False
(1)一个类被认为是其自身的子类
(2)classinfo可以是类对象组成的元组,只要class是其中任何一个候选类的子类,则返回True.
(3)在其他情况下,会抛出一个TypeError异常
'''

class A:
    pass
class B(A):
    pass

print(issubclass(B ,A))  # True
print(issubclass(B ,B ))  # True
print(issubclass(B,object)) #object是所有类的基类
# True

class C:
    pass

print(issubclass(B ,C))    # True

2、isinstance(object,classinfo)

isinstance(object,classinfo)
"""
如果第一个参数(object)是第二个参数(classinfo)的实例对象,则返回True,否则返回False:
(1)如果object是classinfo的子类的一个实例,也符合条件
(2)如果第一个参数不是对象,则永远返回False
(3)classinfo可以是类对象组成的元组,只要object是其中任何一个候选对象的实例,则返回 True
(4)如果第二个参数不是类或者由于类对象组成的元组,会抛出一个TypeError异常
"""
class A:
    pass
class B(A):
    pass
class C:
    pass

print(isinstance(B ,C))   # False

b1 = B()
print(isinstance(b1,B))  # True

print(isinstance(b1,C)) # False

print(isinstance(b1,A)) # True


print(isinstance(b1,(A,B,C)))  # True

 3、hasattr(object,name)

hasattr(object,name)

#作用就是测试一个对象里是否有指定的属性
#第一个参数(object)是对象,第二个参数(name)是属性名(属性的字符串名字)

class C:
    def __init__(self,x =0):
        self.x = x

c1 = C()

print(hasattr(c1,'x'))   #注意,属性名要用引号扩起来
#True

4、getattr(object,name[,default])

getattr(object,name[,default])
#返回对象指定的属性值,如果指定的属性不存在,则返回default(可选参数)的值;若没有设置default参数,则抛出ArttributeError异常。

class C:
    def __init__(self,x =0):
        self.x = x

c1 = C()

print(getattr(c1,'x'))   #  0
print(getattr(c1,'y'))   # AttributeError: 'C' object has no attribute 'y'
print(getattr(c1,'y','您所访问的属性不存在...'))
# 您所访问的属性不存在...

5、setattr(object,name,value)

setattr(object,name,value)
#设置对象中指定属性的值,如果指定的属性不存在,则会新建属性并赋值

class C:
    def __init__(self,x =0):
        self.x = x

c1 = C()
setattr(c1,'y','Fishc')

print(getattr(c1,'y'))   # Fishc

6、delattr(object,name)

delattr(object,name)
#用于删除对象中指定的属性,如果属性不存在,则会抛出AttributeError异常

class C:
    def __init__(self,x =0):
        self.x = x

c1 = C()
setattr(c1,'y','Fishc')

delattr(c1,'y')
delattr(c1,'z')   # AttributeError: z
print(getattr(c1,'y'))   # Fishc

7、property(fget=None,fset=None,fdel=None,doc=None)

property(fget=None,fset=None,fdel=None,doc=None)
#他的作用是通过属性来设置属性
#第一个参数是获得属性的方法名,第二个参数是设置属性的方法名,第三个参数是删除属性的方法名

class C:
    def __init__(self,size=10):
        self.size = size

    def getSize(self):
        return self.size
    def setSize(self,value):
        self.size = value
    def delSize(self):
        del self.size

    x = property(getSize,setSize,delSize)

c = C()
print(c.x)
c.x = 13
print(c.size)

del c.x
print(c.size)  # AttributeError: 'C' object has no attribute 'size'

 

9、作业

[课后作业] 第036讲:类和对象:给大家介绍对象 | 课后测试题及答案

[课后作业] 第037讲:类和对象:面向对象编程 | 课后测试题及答案

[课后作业] 第038讲:类和对象:继承 | 课后测试题及答案

[课后作业] 第039讲:类和对象:拾遗 | 课后测试题及答案

[课后作业] 第040讲:类和对象:一些相关的BIF | 课后测试题及答案

 

10、扩展阅读

 

posted on 2018-12-31 16:42  pf42280  阅读(643)  评论(0)    收藏  举报