面向对象编程(三)

一.组合

    1.什么是组合

       组合指的是某一个对象拥有一个属性,该属性的值是另外一个类的对象

class Foo:
      pass

class Bar:
      pass

obj=Foo()
obj.attr=Bar()

obj.xxx
obj.attr.yyy

    2.为何要用组合

      通过为某一个对象添加属性(属性的值是另外一个类的对象)的方式,可以间接地将两个类关联/整合/组合到一起从而减少类与类之间代码冗余

 class Foo1:
        pass
 class Foo2:
        pass
 class Foo3:
        pass

 class Bar:
        pass

 obj_from_bar=Bar()

 obj1=Foo1()
 obj2=Foo2()
 obj3=Foo3()

 obj1.attr1=obj_from_bar
 obj2.attr2=obj_from_bar
 obj3.attr3=obj_from_bar

  3. 如何用组合  

class OldboyPeople:
    school = 'Oldboy'

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

class OldboyStudent(OldboyPeople):
    def __init__(self, name, age, sex,score):
        OldboyPeople.__init__(self,name,age,sex)
        self.score = score

    def choose_course(self):
        print('%s choosing course' % self.name)

class OldboyTeacher(OldboyPeople):
    def __init__(self,name,age,sex,level,salary):
            OldboyPeople.__init__(self,name,age,sex)
            self.level=level
            self.salary=salary

    def score(self,stu,num):
        stu.score=num

class Course:
    def __init__(self,c_name,c_price,c_period):
        self.c_name = c_name
        self.c_price = c_price
        self.c_period = c_period

    def tell_info(self):
        print('<课程名:%s 价钱:%s 周期:%s>' %(self.c_name,self.c_price,self.c_period))
        
# 创建课程对象
python=Course('python全栈开发',1900,'5mons')
linux=Course('linux架构师',900,'3mons')

stu1=OldboyStudent('刘二蛋',38,'male')
stu1.course=python
# print(stu1.__dict__)

stu1.course.tell_info()

tea1=OldboyTeacher('egon',18,'male',10)
tea1.course=python
# print(tea1.__dict__)
tea1.course.tell_info()
#===============================================================================================================
class OldboyPeople:
    school = 'Oldboy'

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

class OldboyStudent(OldboyPeople):
    def __init__(self, name, age, sex,score=0):
        OldboyPeople.__init__(self,name,age,sex)
        self.score = score
        self.courses=[]

    def choose_course(self):
        print('%s choosing course' % self.name)

    def tell_all_course(self):
        print(('学生[%s]选修的课程如下' %self.name).center(50,'='))
        for obj in self.courses:
            obj.tell_info()
        print('='*60)

class OldboyTeacher(OldboyPeople):
    def __init__(self,name,age,sex,level):
        OldboyPeople.__init__(self,name,age,sex)
        self.level=level
        self.courses=[]

    def score(self,stu,num):
        stu.score=num

    def tell_all_course(self):
        print(('老师[%s]教授的课程如下' %self.name).center(50,'*'))
        for obj in self.courses:
            obj.tell_info()
        print('*'*70)

class Course:
    def __init__(self,c_name,c_price,c_period):
        self.c_name = c_name
        self.c_price = c_price
        self.c_period = c_period

    def tell_info(self):
        print('<课程名:%s 价钱:%s 周期:%s>' %(self.c_name,self.c_price,self.c_period))

# 创建课程对象
python=Course('python全栈开发',1900,'5mons')
linux=Course('linux架构师',900,'3mons')

stu1=OldboyStudent('刘二蛋',38,'male')
stu1.courses.append(python)
stu1.courses.append(linux)
# print(stu1.courses)
stu1.tell_all_course()

tea1=OldboyTeacher('egon',18,'male',10)
tea1.courses.append(python)
# print(tea1.courses)
tea1.tell_all_course()

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

二.封装

     1.什么是封装

       装:往容器/名称空间里存入名字
       封:代表将存放于名称空间中的名字给藏起来,这种隐藏对外不对内   

      2.为何要封装

          封装数据属性:将数据属性隐藏起来,类外就无法直接操作属性,需要类内开辟一个接口来外部的使用可以间接地操作属性,可以在接口内定义任意的控制逻辑,从而严格控制使用对属性的操作

          封装函数属性:隔离复杂度

      3.如何封装

         在类内定义的属性前加__开头(没有__结束)
       总结:
        1. __开头的属性实现的隐藏仅仅只是一种语法意义上的变形,并不会真的限制类外部的访问
        2. 该变形操作只在类定义阶段检测语法时发生一次,类定义阶段之后新增的__开头的属性并不会变形
        3. 如果父类不想让子类覆盖自己的属性,可以在属性前加__开头         

class A:
    __N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N
    def __init__(self):
        self.__X=10 #变形为self._A__X
    def __foo(self): #变形为_A__foo
        print('from A')
    def bar(self):
        self.__foo() #只有在类内部才可以通过__foo的形式访问到.

#A._A__N是可以访问到的,
#这种,在外部是无法通过__x这个名字访问到。

# 正常情况
class A:
    def fa(self):
        print('from A')


    def test(self):
        self.fa()

class B(A):
     def fa(self):
        print('from B')

b = B()
b.test() #from B


# 把fa定义成私有的,即__fa
class A:
   def __fa(self):  # 在定义时就变形为_A__fa
        print('from A')
 
   def test(self):
       self.__fa()  # 只会与自己所在的类为准,即调用_A__fa


class B(A):
    def __fa(self):
        print('from B')

b = B()
b.test()  #from A

  三.特性(property)

       1.什么是特性property

           property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值,可以将类内部的函数属性伪装成一个数据属性

        2.property的用法      

#计算BMI指数
class People:
    def __init__(self,name,weight,height):
        self.name=name
        self.weight=weight
        self.height=height
    @property
    def bmi(self):
        return self.weight / (self.height**2)

p1=People('egon',75,1.85)
print(p1.bmi)
#===========================================================

#圆的周长和面积
import math
class Circle:
    def __init__(self,radius): #圆的半径radius
        self.radius=radius

    @property
    def area(self):
        return math.pi * self.radius**2 #计算面积

    @property
    def perimeter(self):
        return 2*math.pi*self.radius #计算周长

c=Circle(10)
print(c.radius)
print(c.area) #可以向访问数据属性一样去访问area,会触发一个函数的执行,动态计算出一个值
print(c.perimeter) #同上
'''
输出结果:
314.1592653589793
62.83185307179586
'''

#注意:此时的特性arear和perimeter不能被赋值
c.area=3 #为特性area赋值
'''
抛出异常:
AttributeError: can't set attribute
'''

   3. 为什么要用property

         将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则

      除此之外,面向对象封装有三种方式

       [public]:这种其实就是不封装,是对外公开的

       [protected]:这种封装方式对外不公开,但对朋友(friend)或者子类(形象的说法是“儿子”,但我不知道为什么大家 不说“女儿”,就像“parent”本来是“父母”的意     思,但中文都是叫“父类”)公开

       [private]:这种封装对谁都不公开

      python并没有在语法上把它们三个内建到自己的class机制中,在C++里一般会将所有的所有的数据都设置为私有的,然后提供set和get方法(接口)去设置和获取,在python中通过property方法可以实现

class Foo:
    def __init__(self,val):
        self.__NAME=val #将所有的数据属性都隐藏起来

    @property
    def name(self):
        return self.__NAME #obj.name访问的是self.__NAME(这也是真实值的存放位置)

    @name.setter
    def name(self,value):
        if not isinstance(value,str):  #在设定值之前进行类型检查
            raise TypeError('%s must be str' %value)
        self.__NAME=value #通过类型检查后,将值value存放到真实的位置self.__NAME

    @name.deleter  #注意:这个地方.deleter前面的那个值必须要和被property装饰的那个函数的名相同
    def name(self):
        raise TypeError('Can not delete')

f=Foo('egon')
print(f.__dict__)
print(Foo.__dict__)
# f.name=10 #抛出异常'TypeError: 10 must be str'
# del f.name #抛出异常'TypeError: Can not delete'

      

       

 

            

         

         

         

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值