类,封装

2021-09-06  本文已影响0人  慕知

一,面向对象

面向过程:
优点:复杂的过程流程化,简单化
缺点:可扩展性差
举例:linux中shell编译安装,这种固定的流程,面向过程方式堆积命令即可


面向对象:对象是特征与技能的合体
优点:可扩展性强
缺点:编程复杂难度高,容易出现过度设计




二,类

1,类的定义


在程序中:一定先有类再有对象
对象可以访问到类中共有的数据与功能,所以类中的内容仍然是属于对象的;
类只不过是一种节省空间、减少代码冗余的机制,面向对象编程最终的核心仍然是去使用对象。



class Student:
    beauty='mzz'
    def learning(self):
        print('mzz like to study')
    print('----->running')

# ----->running会在"类"定义阶段执行

# 查看类的内存空间
print(Student.__dict__)
# {'__module__': '__main__', 'beauty': 'mzz', 'learning': <function Student.learning at 0x102c43e50>, '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': None}




print(Student.beauty)    # 数据类型(变量)
# mzz
print(Student.learning)     # 函数属性
# <function Student.learning at 0x103063e50>
# 类调用的learning是一个函数,所以要按照函数的调用方式,即要加上self这个参数

Student.learning('mzz')
# ----->running
# mzz like to study








# 增加
Student.counry="China"
print(Student.counry)
# China


# 修改
Student.beauty="Egg"
print(Student.beauty)
# Egg


# 删除
del Student.counry
#print(Student.counry) # 会报错,已经删除

# 查看类的内存空间验证,没有country
print(Student.__dict__)

2,类的实例化过程

# 调用类的过程又称实例化
#1)得到一个返回值,即对象,该对象是一个空对象
#2) Student.__init__,把类当作第一个参数传进去
class Student:
    beauty='mzz'

    def __init__(self,name,age,sex):  # 在调用时自动触发
        self.Name=name
        self.Age=age
        self.Sex=sex

    def learning(self):
        print('mzz like to study')


s1=Student('mz',13,'female')
print(s1.__dict__)
# {'Name': 'mz', 'Age': 13, 'Sex': 'female'}

print(s1.Sex)
# female


s2=Student('egon',22,'male')
print(s2.Name)
# egon


3,属性查找与绑定方法

#1, 查找一个对象的属性,先找自己的__dict__,再找类的__dict__
class Student:
    school='Asia'

    def __init__(self,name,age,sex):  # 在调用时自动触发
        self.Name=name
        self.Age=age
        self.Sex=sex

    def learning(self):
        print('%s like to study' %self.Name)

s1=Student('mz',13,'female')
s2=Student('egon',22,'male')





#2, 类的数据属性是所有对象共享,所有对象都指向同一个内存地址
print(Student.school,id(s1.school))
# Asia 4303951152
print(Student.school,id(s2.school))
# Asia 4303951152



Student.school='India'
print(Student.school,id(s1.school))
# India 4383185008
print(Student.school,id(s2.school))
# India 4383185008









#3, 类中定义的函数是绑定给对象使用,类也可以使用(用起来会很复杂)
# 不同对象就是不同绑定方法
# 绑定给谁,就应该给谁调用

print(Student.learning)
# <function Student.learning at 0x10029f790>
print(s1.learning)
# <bound method Student.learning of <__main__.Student object at 0x10025dfa0>>
print(s2.learning)
# <bound method Student.learning of <__main__.Student object at 0x1002962b0>>


s1.learning()
# mz like to study
s2.learning()
# egon like to study






# 接上验证,函数属性的绑定方法
class Student:
    school='Asia'

    def __init__(self,name,age,sex):  # 在调用时自动触发
        self.Name=name
        self.Age=age
        self.Sex=sex

    def learning(self,x,y):
        print('%s like to study' %self.Name)
        print(x,y)


s1=Student('mz',13,'female')
s2=Student('egon',22,'male')

s1.learning(1,2)
# mz like to study
# 1 2


s2.learning(1,2)
# egon like to study
# 1 2


#类也可以使用(用起来会很复杂)
Student.learning(s1,1,2)
# mz like to study
# 1 2



类的函数属性调用默认必须要有一个参数

示例

# 示例:任何一个老师都可以看到老师的个数
class Teacher:
    school='Asia'
    count=0

    def __init__(self,name,sex,age,level,salary):
        self.name=name
        self.age=age
        self.level=level
        self.salary=salary
        Teacher.count+=1

    def teach(self):
        print('%s is teaching' %self.name)


t1=Teacher('小艾','male',20,1,3000)
t2=Teacher('小林','male',21,2,4000)
t3=Teacher('小狗','female',23,3,5000)


# 查看Teacher数量
print(t1.count)
print(t2.count)
print(t3.count)
# 3   都是3

4,类和对象

a1=list([1,2,3,4])
a2=list([1,2,3,4])
print(id(a1))
# 4343027392

print(id(a2))
# 4343027456

a1.append('a')
a2.append('b')

print(a1)
# [1, 2, 3, 4, 'a']

print(a2)
# [1, 2, 3, 4, 'b']

python中,一切皆对象,类型本质都是类。
数据类型都是一个个类创造的对象

-- 游戏中生命值攻击力示例

class Dog:
    camp='A' #所有类里面的(xiaohua)都属于A组
    def __init__(self,name,life_value=200,attack_value=10):
    # 初始生命值200,初始攻击力10
        self.name=name
        # 赋值,前面的name可以任意起名
        self.life_value=life_value
        self.attack_value=attack_value
    def attack(self,enemy):   # 攻击技能
        enemy.life_value-=self.attack_value
        # 敌人的生命力=敌人的生命力减去自己的攻击力

class Cat:
    camp='B'
    def __init__(self,name,life_value=100,attack_value=80):
        self.name=name
        self.life_value=life_value
        self.attack_value=attack_value
    def attack(self,enemy):
        enemy.life_value-=self.attack_value

xiaohua=Dog('xiaohua')
mimi=Cat('mimi')


print(xiaohua.life_value)
# 200

mimi.attack(xiaohua)
print(xiaohua.life_value)
# 120

5,代码级别看面向对象

# 伪代码示例

#1、在没有学习类这个概念时,数据与功能是分离的
def exc1(host,port,db,charset):
    conn=connect(host,port,db,charset)
    conn.execute(sql)
    return xxx


def exc2(host,port,db,charset,proc_name):
    conn=connect(host,port,db,charset)
    conn.call_proc(sql)
    return xxx

#每次调用都需要重复传入一堆参数
exc1('127.0.0.1',3306,'db1','utf8','select * from tb1;')
exc2('127.0.0.1',3306,'db1','utf8','存储过程的名字')



#2、我们能想到的解决方法是,把这些变量都定义成全局变量
HOST=‘127.0.0.1’
PORT=3306
DB=‘db1’
CHARSET=‘utf8’

def exc1(host,port,db,charset):
    conn=connect(host,port,db,charset)
    conn.execute(sql)
    return xxx


def exc2(host,port,db,charset,proc_name)
    conn=connect(host,port,db,charset)
    conn.call_proc(sql)
    return xxx

exc1(HOST,PORT,DB,CHARSET,'select * from tb1;')
exc2(HOST,PORT,DB,CHARSET,'存储过程的名字')





#3、但是2的解决方法也是有问题的,按照2的思路,我们将会定义一大堆全局变量,这些全局变量并没有做任何区分,即能够被所有功能使用,然而事实上只有HOST,PORT,DB,CHARSET是给exc1和exc2这两个功能用的。言外之意:我们必须找出一种能够将数据与操作数据的方法组合到一起的解决方法,这就是我们说的类了

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


    def exc2(self,sql):
        conn=connect(self.host,self.port,self.db,self.charset)
        res=conn.call_proc(sql)
        return res


三,封装

1,隐藏属性

# 正常情况下,x可以通过调用Foo正常访问
class Foo:
    x=1

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

print(Foo.x)
# 1

abc=Foo('dog',3)
print(abc.age)
# 3





# 隐藏属性,在需要隐藏属性的前面加上__(两个下划线)
# 隐藏属性是在调用阶段生成,所以在类内部是可以直接访问双下滑线开头的属性的
class Foo:
    __x=1

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


#print(Foo.x)  会报错

obj=Foo('mzz',18)
print(obj.name,obj.age)
# mzz 18



class Foo:
    __x=1

    def __init__(self,name,age):
        self._name=name
        self._age=age

print(Foo.__dict__)
# {'__module__': '__main__', '_Foo__x': 1, '__init__': <function Foo.__init__ at 0x10304f790>, '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None}

# 根据属性的变形,通过以下方式可以得到隐藏的属性的值
print(Foo._Foo__x)
# 1


2,为什么使用隐藏属性




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



#People.get_name('mmm')   # 会报错

obj=People('muzz')
#print(obj.set_name())   # 会报错


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

    def get_name(self):
        # 类似于开个接口,可以赋予其他的东西
        print('谁是小可爱')
        print(self.__name)

obj=People('muzz')
obj.get_name()
# 谁是小可爱
# muzz


# 类的创造者,可以严格控制使用者对属性的操作
class People:
    def __init__(self,name):
        self.__name=name

    def get_name(self):
        # 类似于开个接口,可以赋予其他的东西
        print('不给看')

obj=People('muzz')
obj.get_name()
# 不给看



# 如果更改名字,如下
class People:
    def __init__(self,name):
        self.__name=name

    def get_name(self):
        print(self.__name)

    def set_name(self,val):
        self.__name=val

obj=People('muzz')
obj.set_name('egon')
obj.get_name()
# egon





# 增加条件
class People:
    def __init__(self,name):
        self.__name=name

    def get_name(self):
        print(self.__name)

    def set_name(self,val):
        if type(val) is not str:
            print('必须字符串类型')
            return
        self.__name=val

obj=People('muzz')
obj.set_name(12235)
obj.get_name()
# 必须字符串类型
# muzz


3,property装饰器

1)用法一
"""
成人的BMI数值:
过轻:低于18.5
正常:18.5-23.9
过重:24-27
肥胖:28-32
非常肥胖, 高于32
  体质指数(BMI)=体重(kg)÷身高^2(m)
  EX:70kg÷(1.75×1.75)=22.86
"""


# 例1:传值给People,得到ami的值需要obj.ami(),即调用函数的方式
class People:
    def __init__(self,name,weight,height):
        self.name=name
        self.weight=weight
        self.height=height

    def ami(self):
        print(self.weight / (self.height **2))

obj=People('mzz',55,1.70)
print(obj.ami())
# 19.031141868512112
# None

obj.ami()
# 19.031141868512112






# 例2:加上@property 可以正常以obj.ami方式调用
class People:
    def __init__(self,name,weight,height):
        self.name=name
        self.weight=weight
        self.height=height

    @property
    def ami(self):
        print(self.weight / (self.height **2))

obj=People('mz',55,1.70)
obj.ami
# 19.031141868512112


2)用法2
# property用法2
class People:
    def __init__(self, name):
        self.__name = name

    def get_name(self): # obj1.name
        return self.__name

    def set_name(self, val): # obj1.name='EGON'
        if type(val) is not str:
            print('必须传入str类型')
            return
        self.__name = val

    def del_name(self): # del obj1.name
        del self.__name

    name=property(get_name,set_name,del_name)

obj=People('eggg')
print(obj.name)

obj.set_name('EGGG')
print(obj.name)
#EGGG

obj.del_name()
print(obj.name) # 会报错

3) 用法3 (建议)
# property用法3
class People:
    def __init__(self, name):
        self.__name = name

    @property
    def name(self): # obj1.name
        return self.__name

    @name.setter
    def name(self, val): # obj1.name='EGON'
        if type(val) is not str:
            print('必须传入str类型')
            return
        self.__name = val

    @name.deleter
    def name(self): # del obj1.name
        print('不让删除')
obj=People('mz')
print(obj.name)
# mz

obj.name=12
# 必须传入str类型

obj.name='xiaohua'
print(obj.name)
# xiaohua

del obj.name
# 不让删除




注意def name的name要统一
上一篇下一篇

猜你喜欢

热点阅读