面向对象之核心封装

今日学习总结:

 

一、组合:

1.什么是组合?

   组合指的是一个对象中,包含另外一个或多个对象

2.为什么要用组合?

减少代码的冗余

3.如何使用组合?

耦合度:藕断丝连

耦合度越高,程序的可扩展性越低

耦合度越低,程序的可扩展性越高。

总结:

继承是类与类的关系,子类继承父类的属性/方法,子类与父类是一种‘从属’关系。

组合是对象与对象的关系,一个对象拥有另一个对象中的属性/方法,是一种 什么有什么的关系。

 

例子:组合的使用

#组合的实现
#父类
class People:
    def __init__(self,name,age,sex):
        self.name=name
        self.age=age
        self.sex=sex

#老师类
class Teacher(People):
    def __init__(self,name,age,sex):
        super().__init__(name,age,sex)

#学生类
class Student(People):
    def __init__(self,name,age,sex):
        super().__init__(name,age,sex)
#日期类
class Date:
    def __init__(self,year,month,day):
         self.year=year
         self.month=month
         self.day=day
    def tell_birth(self):
        print(
            f'''
            ====出生年月日====
            年:{self.year}
            月:{self.month}
            日:{self.day}
            
            '''
        )

tea1=Teacher('tank',17,'male')  #调用老师类,产生老师类的对象
date_obj=Date(1999,7,7)         #调用日期类,产生日期类的对象
print(tea1.name,tea1.age,tea1.sex)
tea1.date_obj=date_obj          #把日期类的对象赋给了tea1.date_obj(老师类的对象中包含一个日期类的对象)
                                #也就是给老师类对象tea1 中加了一个属性叫 date_obj属性、属性的值是对象(date_obj)
tea1.date_obj.tell_birth()      #调用老师类对象(tea1)中的 对象属性(date_obj) 的 属性(tell_birth)

 

例子:继承使用

#继承实现
#父类
class People:
    def __init__(self, name, age, sex, year, month, day):
        self.name = name
        self.age = age
        self.sex = sex
        self.year = year
        self.month = month
        self.day = day

    def tell_birth(self):
        print(f'''
        ===== 出生年月日 =====
            年: {self.year}
            月: {self.month}
            日: {self.day}
        ''')


# 老师类
class Teacher(People):
    def __init__(self, name, age, sex, year, month, day):
        super().__init__(name, age, sex, year, month, day)


# 学生类
class Student(People):
    def __init__(self, name, age, sex, year, month, day):
        super().__init__(name, age, sex, year, month, day)


tea1 = Teacher('tank', 17, 'male', 2002, 6, 6)
stu1 = Student('HCY', 109, 'female', 1910, 11, 11)

print(tea1.name, tea1.age, tea1.sex)
tea1.tell_birth()                        #表示调用父类的 属性(tea1_birth)进而输出,,
print(stu1.name, stu1.age, stu1.sex)
stu1.tell_birth()

例子:组合的练习:

选课系统: 1.有学生、老师类,学生与老师有属性‘名字、年龄,性别,课程’

                    2.有方法 老师、学生可以添加课程,打印学习/教授课程

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

    def add_course(self, course_obj):
        self.course_list.append(course_obj)              #给父类 添加一个 课程列表属性(course_list),然后在在这个列表中 添加课程对象(course_obj)

    def tell_all_course_info(self):
        # 从当前对象中课程列表中取出所有的课程对象
        for course_msg in self.course_list:             # course_msg 变量其实就是 一个个 课程对象 。
            # 通过课程对象.打印课程信息方法
            course_msg.tell_course_info()               # 因为 course_msg 是课程对象,所以可以调用 课程类中 tell_course_info 的属性,输出。。。

class Student(People):
    def __init__(self, name, age, sex):
        super().__init__(name, age, sex)
        self.course_list = []

class Teacher(People):
    def __init__(self, name, age, sex):
        super().__init__(name, age, sex)
        self.course_list = []

class Course:
    def __init__(self, course_name, course_price, course_time):
        self.course_name = course_name
        self.course_price = course_price
        self.course_time = course_time

    def tell_course_info(self):
        print(f'''
        ====== 课程信息如下 ======
        课程名称: {self.course_name}
        课程价格: {self.course_price}
        课程周期: {self.course_time}
        ''')
# 创建学生对象
stu1 = Student('HCY', 2000, 'female & male')
# 创建课程对象
python_obj = Course('python', 77777, 6)   #调用课程类 产生 课程对象
go_obj = Course('go', 88888, 4)

stu1.add_course(python_obj)              # 调用父类的 添加课程(add_course)属性。把课程类 的对象当成参数传进去。进而实现了组合
stu1.add_course(go_obj)
# 当前学生打印所有课程信息
stu1.tell_all_course_info()              #调用 父类中 打印所有课程信息的 (tell_all_course_info) 属性

 

二、封装

1.什么是封装?

 封:好比一个袋子 。                                      比如:对象

 装:好比将一堆小猫、小狗等等装在袋子里。比如:属性和方法

封装:指的是可以将一堆属性和方法,封装到对象中。

ps:对象就好比一个‘袋子/容器’,可以存放一堆属性和方法。

ps:存不是目的,目的是为了取,可以通过‘对象’的方法获取属性和方法。

2.为什么要封装?

    可以通过‘对象.属性或方法’的方式 ‘存取/获取’ 属性和方法

    优点:对象拥有‘.’的机制,方便存取数据。

       class User:
            x = 10
            def func():
                pass
        obj = User()  #调用类 产生一个类的对象 ,这个对象像一个袋子,里面有属性 x , y ,func
        obj.y = 20    #对象拥有 . 的机制
        
        print(obj.x)
        print(obj.y)

3.如何封装?

  看例子:

class User:
    x = 10
    def func(self):
        pass

obj = User()  # 调用类 产生一个类的对象 ,这个对象像一个袋子,里面有属性 x , y ,func
obj.y = 20    # 对象拥有 . 的机制。 另外这是给 类的一个对象添加属性  不是给类

print(obj.x)  #10
print(obj.y)  #20
print(obj)    #这是类的 对象,所以结果是  <__main__.User object at 0x000000000211DEB8>
print(User)   #这是类名,所以结果是 <class '__main__.User'>
print(obj.__dict__) #查看类的对象中的 属性 。结果是:{'y': 20}
print(User.__dict__)#查看类中的属性 。结果是 {'__module__': '__main__', 'x': 10, 'func': <function User.func at 0x0000000009EE27B8>, '__dict__': <attribute '__dict__' of 'User' objects>, 
                    # '__weakref__': <attribute '__weakref__' of 'User' objects>, '__doc__': None}

 

三、访问限制机制

1.什么是访问限制机制?

01.凡是在类 内部定义的属性或方法,以__开头的属性或方法名,都会被限制,外部不能‘直接访问’该属性原型

02.python特有的:凡是在类内部定义_ _开头的属性或方法,都会变形为_类名_ _属性/方法

2.为什么要有访问机制?

比如:将一些隐私的数据,隐藏起来不让外部轻易获取。

接口:可以将一些数据封装成一个接口,可以让用户访问接口,并且通过相应的逻辑,最后再将数据返回给用户。

class User:
    __name = 'tank'
    __age = 17
    __sex = 'male'
    __ID = '46549846798468498498'
    __bal = 151651651651654654654
    # 校验接口,获取用户信息
    def parse_user(self, username, password):
        if username == 'tank_jam' and password == '123':
            print(f'''
            通过验证,获取用户信息。
            用户名: {self.__name}
            用户年龄: {self.__age}
            用户性别: {self.__sex}
            身份ID: {self.__ID}
            用户资产: {self.__bal}
            ''')
        else:
            print('校验失败, 无法查询用户信息!')

    # __开头的方法
    def __run(self):
        print('tank is running...')

obj = User()                          #调用类产生 类的对象
obj.parse_user('tank_jam', '123')     #调用类的 parse_user函数

 

 

3.如何实现?

class User:
    __name='tank'           #这里内部 会变形为   _User_ _name  

    __age=78                #这里内部 会变成    _User_ _age   
    def __run(self):        #这里内部 会变成    _User_ _run(self)
        print('tank今年78岁了')

# print(User.__age)
# obj=User()
# print(obj.__age)

print(User._User__name)
User._User__run(123)        #这里需要传实参      
obj=User() 
print(obj._User__name)
obj._User__run()
#这里不需要参数

四、property

1.什么是property ?

01.是一个python内置的装饰器,可装饰在类内部的方法上。

02.可以将该方法调用方式:对象.方法()——》对象.方法

2.为什么用property?

让名词的方法,调用时更为合理。
目的是为了,迷惑调用者,调用的方法误以为是 属性.

PS: 在某些场景下,调用的方法只是用来获取计算后的某个值。
PS: 必须通过 对象.方法() 方式调用,让该方法看起来像动词

3.如何使用?

class User:
    def __init__(self, name, weight, height):
        self.__name = name
        self.weight = weight
        self.height = height

    # 获取bmi指数方法
    @property
    def bmi(self):
        return self.weight / (self.height ** 2)

user_obj = User('HCY', 100, 1.9)

# print(user_obj.bmi())
print(user_obj.bmi)            #省略了小括号()

 

例子:

class Foo:
    def __init__(self,val):
        self.__NAME=val             #将属性隐藏起来
    @property
    def name(self):
       return self.__NAME
obj=Foo('lili')
print(obj._Foo__NAME)               #因为__NAME 所以调用时 要加上 _类名
print(obj.name)                     #因为有了property 所以调用函数name时,不需要小括号()

结果:
lili

例子:修改,删除

class Foo:
    def __init__(self,val):
        self.__NAME=val             #将属性隐藏起来
    @property
    def name(self):
       return self.__NAME
    @name.setter
    def name(self,value):
       if not isinstance(value,str):  #在设定值之前进行类型检查 isinstance() 函数来判断一个对象是否是一个已知的类型
           raise TypeError('%s must be str' %value)
       self.__NAME=value              #通过类型检查后,将值value存放到真实的位置self.__NAME
    @name.deleter
    def name(self):
                                        #raise PermissionError('Can not delete')
        del self.__NAME
obj=Foo('lili')
print(obj._Foo__NAME)                   #lili

obj.name='tank'                         #触发name.setter装饰器对应的函数name(obj,'tank')
print(obj._Foo__NAME)                   #tank

del obj.name                            #触发name.deleter对应的函数name(obj),抛出异常PermissionError

 

五、python中的__init__() 方法:

在Python中定义类经常会用到__init__函数(方法),首先需要理解的是,两个下划线开头的函数是声明该属性为私有,不能在类的外部被使用或访问。而__init__函数(方法)支持带参数类的初始化,也可为声明该类的属性(类中的变量)。

例子:__init__函数(方法)的第一个参数必须为self,后续参数为自己定义。

demo1:

class User:
    name='tank'
    age=78
    def run(self,sal):          #self 是指 :谁调用函数,谁就是self
        print('tank今年78岁了')
        self.sal=sal            #这里 self就是等于obj 所以这是给obj添加了一个属性

obj=User()
obj.run(12000)                  #这是类的对象去调用 类的函数方法,不需要传参。当函数的形参不是一个时候,要传实参
print(obj.sal)                  #结果:12000

demo2:

class User:
    name='tank'
    age=78
    def __init__(self,sal):         #self 是指 :谁调用函数,谁就是self
        print('tank今年78岁了')
        self.sal=sal                #这里 self就是等于obj 所以这是给obj添加了一个属性
obj=User(9999)                      #只有调用类 时 才会触发__init__函数
print(obj.sal)                      #9999

 

六、__init__ 与__new__的区别:

__init__(self) 初始化,__new__实例化方法,两者执行的顺序,先有实例,才能初始化。

__new__ (构造函数)单独地创建一个对象,而 __init__ (初始化函数)负责初始化这个对象。

__new__ 负责对象的创建而 __init__ 负责对象的初始化

 

posted @ 2019-11-27 18:14  薛定谔的猫66  阅读(210)  评论(0)    收藏  举报