面对对象基础

在计算机创建一个人类对象

要得到对象,必须先告诉计算机,这个对象具备什么特征和行为,所以需要先定义类

类名要按照大驼峰的方式来书写 ThisIsPerson 每个单词首字母大写

class People

在类中描述对象的特征和行为

class Person

  name='李四'

  sex=‘male’

  age=‘20’

通过调用类,得到对象,也称之为实例化 或创建对象

obj=Person()

print(obj)

这是一个Person类的对象,打印出来是它的地址

使用对象的属性(说的就是特征)
print(obj.name)
print(obj.age)
class Student:
    number = "007"
    name = "盖伦"
    sex = "man"
    age = 18

    # 学生的学校 由于每个学生的学校都是相同 所以将其放到类中
    school = "Tsinghua"



# 创建了两个学生对象
stu1 = Student()
stu2 = Student()

print(stu1)
print(stu2)
# 每个对象内存地址都是不同的 , 在创建对象时,计算机会申请一个新的内存空间,并将对象中的内容存进去

print(id(stu1.name))
print(id(stu2.name))

# 由于name的值时声明在类中的,所以每个对象使用的都是同一份


print(stu1.name)
print(stu2.name)

# 为对象单独制定属性
stu1.name = "韩信"
stu2.name = "陈大炮"


print(stu1.name)
print(stu2.name)


# 每个对象的name属性都不同,则意味需要给每个对象单独指定name



# 存储属性的位置有两个 一个是类中,还有一个对象中
# 当每个对象的某个特征都相同时则放到类中
# 当每个对象的某个特征都不同时则放到对象中

# 通过__dict__可以获取一个对象中包含的内容
stu1.age = 30
print(stu1.__dict__)
print(stu2.__dict__)


# 获取类中包含的内容
print(Student.__dict__)







# 如何为对象增加属性
# 存储属性的位置有两个 类 和对象
"属性的访问顺序"
class Car:
    c_type = "破鞋"
    color = "red"
    price = 400000


c1 = Car()
c2 = Car()

print(c1.__dict__)
print(c2.__dict__)
print(c1.c_type)

# 当对象中不存在是会到类中去找

c1.c_type = "法拉利"
print(c1.__dict__)
print(c2.__dict__)
print(c1.c_type)

print(c2.c_type)
# 如果对象中存在这个属性,优先访问对象中的属性
print(Car.__dict__)

# 查找顺序为 对象 ->  类

# 当创建一个类的时候 会产生名称空间,存储类中名称和值的绑定关系
# 当创建一个对象的时候 会产生名称空间,存储对象中名称和值的绑定关系
# 类还有另一个作用 就是 作为对象的模板,所有属于同一个类的对象,都具备类中的公共内容

# 即使我们什么都不写 类中也存在一些自带的属性,是从父类得到的(继承会详细讨论)
class A:
    pass


print(A.__dict__)
属性的访问顺序
# class Student:
#     # 定义一个函数 用于为对象设置属性
#     def set_attr(obj, name, sex, age):
#         obj.name = name
#         obj.sex = sex
#         obj.age = age
#     pass
#
#
# stu1 = Student()
# # 指定属性
# stu1.name = "张无忌"
# stu1.sex = "man"
# stu1.age = 18
#
#
# stu2 =Student()
# stu2.name = "周芷若"
# stu2.sex = "woman"
# stu2.age = 78
#
#
#
#
# # stu3 = Student()
# # stu4 = Student()
#
#
# # set_attr(stu3,"灭局","woman",88)
# # set_attr(stu4,"金毛狮王","man",20)
#
#
# # print(stu3.__dict__)
# # print(stu4.__dict__)
#
#
# # set_attr这个函数目的是用于设置对象的属性 ,如果没有对象则该函数没有存在的意义,也就是初始化函数与类应该是一个整体
# # 应该讲这个函数放到类中
# stu3 = Student()
# stu4 = Student()
#
#
# Student.set_attr(stu3,"金毛狮王","man",80)
# print(stu3.__dict__)
#
#
# Student.set_attr(stu4,"张全蛋","man",28)
# print(stu4.__dict__)
#
#
# # 现在已经简化了代码 但是对象的创建和初始化步骤是分开的, 通常对象一旦创建 就应该进行初始化,所以最好时将创建与初始化进绑定
#
# stu5 = Student()
# print(stu5)
#





# 作为一个人 一旦出生 性别必须要指定
# 带有__开头__结尾的函数 是一些特殊内置函数,会在某个时间点自动触发执行
class Person:
    # 初始化函数名称是固定  该函数会在调用类是时自动执行,self参数必须有,表示要进行初始化的对象,系统会自动传值
    def __init__(self,name,age):
        print("执行了 __init__")
        print(self)
        self.name = name
        self.age =age

p1 = Person("张三丰",80)

print(p1.__dict__)

p2 = Person("李狗蛋",20)
print(p2.__dict__)

# init 函数用于初始化对象,它会在创建对象时,自动执行,并传入调用类时传递的参数,第一个参数表示要初始化的对象本身,
# self(第一个)参数不需要手动传递
# self表示对象自己 是一个形式参数,名字可以随便取,但是不建议修改
初始化函数
"""
    绑定方法
    什么是绑定 把两个东西捆绑在一起
    什么是方法 方法 就是 函数
        函数是专业术语,不好理解,面向对象编程思想,是要我们模仿现实生活中的抽象概念,为了方便理解就把函数称之为方法

    绑定方法就是 把对象与函数进行绑定
    为什么要把把对象与函数进行绑定ee4444444444 5rr     6   565
        调用函数 就变成了调用对象的方法

    对象本质上就是一种存放数据的容器
    函数是用于处理数据的代码
    绑定方法就是将数据与处理数据的函数绑定在一起

    最终问题是 为什么要把数据与处理数据的函数绑定在一起?

    如何使用绑定方法
"""
class Student:
    school = "BeiJing"

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

    def learning(self):
        print("正在学习..")

    def sayHI(self):
        print("hello my name is %s my age:%s my sex:%s" % (self.name,self.age,self.sex))

    # 默认情况下 在类中定义的函数都是绑定方法,共同点是,都会讲对象作为第一个参数self



stu1 = Student("一个学生","man",18)

stu1.sayHI()

Student.sayHI(stu1)

# 当用用对象来调用类中的方法时,默认把对象传入方法中
# 而用类名来调用时,则需要手动传入对象


print(stu1.sayHI)
#<bound method Student.sayHI of <__main__.Student object at 0x000001784F889C50>>
# 这是一个绑定方法,本质上是Student类中的sayHI函数 现在把这个函数绑定给了地址为0x000001784F889C50的对象



stu2 = Student("李四","",19)
stu2.sayHI()
print(stu2.sayHI)

# 只要拿到对象 就同事拿到了数据和处理数据的方法
绑定方法
为什么要绑定方法:
# 第一个问题传递参数,必须手动传递,很有可能传参顺序而发生错误
# 第二个问题每次处理数据 都需要手动传参数
# 第三个问题 当要处理的数据特别的多 就不能再定义为变量了 你可以使用列表出来存储要处理的数据
# 但是 每次处理 都需要先获取数据 在传递给处理数据的函数
# 所以将 要处理的数据与 处理数据的函数进行绑定
class Student:
    school = "beijing"

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

    # 绑定方法分为两种 一种是绑定给对象的,一种绑定给类的



    # @classmethod
    # def print_school_name():
    #     print("学校名为:%s" % Student.school)

    # 绑定给类的方法 使用一个装饰器叫classmethod,必须有一个参数,表示当前类,参数名也可以自己定义,建议不要修改
    @classmethod
    def print_school(cls):  # 输出类里面叫school的属性
        print(cls.school)

    # def print_school2(self):  # 输出类里面叫school的属性
    #     print(self.school)

    # 这是绑定给对象的方法
    def sayHello(self):
        print(self.name, " 说: 你好")



# # Student.print_school_name()
#
# Student.print_school()

# 对象绑定方法  可以使用对象来调用 也可以使用类名来调用
# 在对象调用时会自动传入对象自己
# 类调用时不会自动传参

# 类的绑定方法,对象和类都能调用,并且都会自动传入这个类
# Student.print_school()
# stu1 = Student("印度阿三","woman",20)
# stu1.print_school()

# 如何定义对象绑定方法 以及类的绑定方法
# 在调用时的区别

# 一个方法到底应该绑定给对象还是帮对给类?
# 当要处理的数据包含在类中时,就应该绑定给类
# 当要处理的数据包含在对象中时,就应该绑定给对象


stu10 = Student("赵六","",20)
# Student.print_school2(stu10)



# 有一个Dog类  每一个Dog对象都应该会叫 会跑  请用面向对象来完成


class Dog:
    def __init__(self,nikename,gender,age):
        self.nikename = nikename
        self.gender = gender
        self.age = age

    def run(self):
        print("不好了 ",self.nikename,"跑了 ")

    def bark(self):
        print("",self.nikename,"在瞎叫...")

d1 = Dog("大金毛","母的",2)
d2 = Dog("大黄","公的",3)

d1.run()
d2.bark()



"""
# 类的绑定方法和对象的绑定方法的相同与不同
相同点:
    1.都会自动传值
    2.都可以被类和对象调用

不同点:
    1.对象绑定方法再对象调用时 传的是对象自己 而类绑定方法字自动传的是类自己
    2.第一个参数  个cls 一个叫self


为什么要绑定?

# 第一个问题传递参数,必须手动传递,很有可能传参顺序而发生错误
# 第二个问题每次处理数据 都需要手动传参数
# 第三个问题 当要处理的数据特别的多 就不能再定义为变量了 你可以使用列表出来存储要处理的数据
但是 每次处理 都需要先获取数据 在传递给处理数据的函数
之所以绑定 ,简化代码,提高效率 
"""
绑定给类的方法
class Teacher:

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

    # @staticmethod 用于定义个非绑定方法
    @staticmethod
    def test_func(num):
        print("test_func run!")
        print(num)

Teacher.test_func(1)

t1 = Teacher("矮根","")
t1.test_func(100)
print(t1.test_func)


#  什么是非绑定方法 再类中 即不绑定给类 也不绑定给对象
#  特点:没有自动传参数的效果 ,类和对象向都能调用,就是一个普通函数
#  当你的这个功能不需要访问类的数据 也不需要访问对象的数据,就可以作为一个非绑定方法
#  使用场景较少
非绑定方法
1.什么是继承
继承是一种关系,必须存在两个对象才可能产生这种关系,在现实生活中的继承,王思聪可以继承王健林的财产
被继承的称为父 继承的一方成为子

在程序中继承指的是类与类之间的关系


2.为什么要使用继承
在生活中,通过继承 子可以直接享受父提供内容,例如财产
在程序中,通过继承可以直接使用父类已有的代码

class Parent:
    year = 2018

    def coding(self):
        print("正在编程........")



class Sub(Parent):
    pass

print(Parent.year)

print(Sub.year)

# Sub.coding()


s = Sub()
print(s.year) # 子类可以使用父类中的属性
s.coding() # 子类也可以父类中的函数
继承的使用
class Person:
    def __init__(self ,name ,age ,sex):
        self.name = name
        self.age = age
        self.sex = sex

    def eat(self):
        print("正在吃饭....")

    def study(self):
        print("正在学习....")

class Teacher(Person):

    def teaching(self):
        print("老师正在上课......")

t1 = Teacher("blex" ,30 ,"woman")
t1.eat()
t1.study()


class Student(Person):
    pass

stu1 = Student("张三" ,20 ,"man")
stu1.eat()
stu1.study()

# stu1.teaching()

# 学生继承了老师 减少了重复代码,但是继承到一些学生不应该有的内容
# 正确姿势:抽取公共的父类  抽象
# 抽象是 抽取多个类中相同的部分形成另一个类



# 通过继承 避免了重复代码的编写
# 通过抽象 避免了继承到一些不应该有的内容
# 应该先抽象再继承
# 再抽取过程中 可能会一些跟业务需求无关的类,这是正常的 这些称之为公共父类
# 公共父类的作用是存储多个子类相同属性和技能
继承的好处

派生指的是 子类继承某个父类 并且拥有自己独特的属性或技能   该子类称之为派生类
只要子类中出现了任何新内容,它就是一个派生类
class Person:
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex

    def sayHI(self):
        print("hello 我是%s 今年%s岁 性别:%s" % (self.name,self.age,self.sex))


# Test不能称为派生类 , 因为没与任何独特的内容与父类完全一致
class Test(Person):
    pass

# 派生类属于子类吗?   派生类一定是某个子类
# Student类就成为 Person类的派生类


class Student(Person):
    def __init__(self,name,age,sex,number):
        self.name = name
        self.age = age
        self.sex = sex
        self.number = number

    # 上课
    def up_class(self):
        print("%s 正在上课.....")
class Person:
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex
        print(self)

    def sayHI(self):
        print("hello 我是%s 今年%s岁 性别:%s" % (self.name,self.age,self.sex))


class Student(Person):
    def __init__(self,name,age,sex,number):
        # self.name = name
        # self.age = age
        # self.sex = sex
        #上述代码与父类中完全相同
        Person.__init__(self,name,age,sex)
        self.number = number
    # 上课
    def up_class(self):
        print("%s 正在上课.....")

    def sayHI(self):
        # print("hello 我是%s 今年%s岁 性别:%s" % (self.name,self.age,self.sex))
        # 访问父类中的方法来简化代码
        # 指名道姓
        # Person.sayHI(self)
        super().sayHI()
        print("学号:",self.number)

stu1 = Student("阿三",20,"woman","9527")
# print(stu1)
# print(stu1.name,stu1.age,stu1.sex)
stu1.sayHI()

# super一定用在 存在继承关系的子类中
字类访问父类的方法
存在继承关系的查找方法
#
# class E:
#     a = 5
#
# class A(E):
#     a = 4
#     pass
#
# class B:
#     a = 3
#     pass
#
# class C:
#     a = 2
#     pass
#
# class D(A,B,C):
#     # a = 1
#     pass
#
# d1 = D()
# # d1.a = 10
# print(d1.a)

#按照继承的顺序,从左到右

所有直接继承或间接继承object的类 都是新式类
object 称之为根类 意思是 所有类 都源自于object类
为什么这么设计?
例如:创建对象时,需要申请内存空间,创建新的名称空间,将对象的属性放入名称空间,这一些了复杂的基础操作,都有object来完成
简单地说object提供了一些常用的基础操作
即所有类都属于新式类(在python3中)

在python3中默认所有类都是新式类

而python2中默认是经典类(不会自动继承Object)

class A:
    age = 18

    def f1(self):
        print(" A f1"
        )
    pass
class B(A):
    age1 = 19
    def f1(self):
        self.f1()
        print(" B f1")

    pass

b1 = B()
print(b1.age)

b1.f1()

# 子类出现了与父类重复的名字 称之为覆盖
# 子类出现了与父类不同的名字 称之为拍派生
覆盖
host = '127.0.0.1'
port = 3306
db = 'db1'
charset = "utf-8"

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


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


def exc2(host,port,db,charset,proc_name):
    conn=connect(host,port,db,charset)
    conn.call_proc(proc_name)
    return 123
#每次调用都需要重复传入一堆参数
# exc1('127.0.0.1',3306,'db1','utf8','select * from tb1;')
#
# exc2('127.0.0.1',3306,'db1','utf8','存储过程的名字')



# exc1(host,port,db,charset,"select * from tb1")
# exc2(host,port,db,charset,"过程名称")

class Mysql:

    def __init__(self,host,port,db,charset):
        self.host = host
        self.port = port
        self.db = db
        self.charset = charset

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

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


my1 = Mysql("127.0.0.1",3306,"mydb","utf-8")

my1.exc1("select *from table1")
my1.exc2("名字")


#  精髓 是 将数据和处理数据的代码绑定 成了一个对象
#  只要获取到对象 相应的数据和方法都有了
面向对象的精髓
组合
多个对象放在一起叫组合

组合 也可以降低代码的冗余

# 学生会增加各种各样的新的属性  比如手机  比如电脑, 这样Student中会增加大量的属性和方法
# 后期的维护成本非常高
# 这时就需要使用组合 来完成减少代码冗余

class Phone:
    def __init__(self,phonenumber,operator,address):
        self.phonenumber = phonenumber
        self.operator = operator
        self.address = address

    def call(self):
        print("%s 正在拨号" % self.phonenumber)


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


class Student(Person):
    def __init__(self,name,sex,age,number):
        super().__init__(name, sex, age)
        self.number = number

    def show_info(self):
        # print("name:%s sex:%s, age:%")
        print(self.__dict__)

    def select_cursor(self):
        print("%s 正在选课...." % self.name)

class Teacher(Person):
    def __init__(self,name,sex,age,salary,level):
        super().__init__(name,sex,age)
        self.salary = salary
        self.level = level
    def set_score(self):
        print("%s 正在为学生打分..." % self.name)


stu = Student("乔峰","",38,"007")

# 学生买了一台手机 所以增加一个手机属性
p1 = Phone("1999999999","中国小米移动","上海浦东")

stu.phone = p1

# 学生要打电话
stu.phone.call()

# 通过将手机对象和学生对象进行组合  完成了需求,并且相比较继承而言
# 耦合度低  手机和学生在修改的时候 互不影响
# 同时也减少了代码冗余

# 继承是一种关系  什么是什么   学生是人类  老师是人类    (如果把手机相关内容插入到人类中,相当于学生是手机???)
# 组合是 对象之间的关系   学生对象拥有手机对象
组合
面向对象三大特征之封装
1.什么是封装

生活中的封装 类似工厂 拿个纸箱,装入一堆产品,拿胶带封起来
在程序中,封装指的是将内容隐藏起来, 在面向对象中有什么内容可以被隐藏?,就是属性和方法

注意:封装绝不是单纯的隐藏
封装是指,隐藏内部实现细节,对外提供使用接口

2.为什么要封装
1.提高安全性 (例如:cpu频率 缓冲区大小,电脑,手机,汽车,收音机)
对于封装属性而言,是通过给访问和修改增加额外的逻辑判断来实现的
对于封装方法
2.封装是为了明确区分内部和外部



3.如何使用封装
在属性或方法名称前 加上两个下划线,就可以将其设置为私有的
另外补充: python中权限只有两种,公开(谁都能访问)的和私有(只有自己能访问)的
属性的封装,通过需要提供相应的设置器和访问器

4.什么时候用
5.封装的实现原理

6.封装的特点
被隐藏的内容 在内部是可以直接访问,外部无法访问
class Student:
    def __init__(self,name,sex,age,idCard):
        self.name = name
        self.age = age
        self.sex = sex
        self.__idCard = idCard
    def say_hi(self):
        print("hello i am %s age is : %s sex is %s" %
              (self.name,self.age,self.sex))

    def test(self):
        print(self.__idCard)

    # 可以使用方法 将内部封装的内容返回出去
    def get_idCard(self):
        return self.__idCard

    # 如果直接返回的话 就没必要设置隐藏了 ,谁都可以通过调用方法来获取,我们再方法中加上自己的判断逻辑
    def get_idCard2(self,pwd): # 如果密码正确我就给你身份证号码
        if pwd == "1111":
            return self.__idCard
        else:
            print("滚 你没有资格知道我的身份证...")

    def set_idCard(self,pwd,new_idCard): # 如果密码正确就允许修改
        if pwd == "1111":
            self.__idCard = new_idCard
        else:
            print("滚 你没有资格修改我的身份证...")





stu = Student("步惊云","",20,"3206661998445132")
stu.say_hi()

# print(stu.__idCard)  # 加上__也访问不了

# stu.test() # 但在内部是可以访问的

# idcard = stu.get_idCard()
# print(idcard)
# 即实现了隐藏  又提供了访问的接口 这就是封装的用法
# idcard = stu.get_idCard2("1111")
# print(idcard)


# 身份证填错了 要修改
# stu.__idCard = "123" # 这样是无法修改原来的身份证信息的 而是添加了新的属性
#
# print(stu.__idCard)
# print(stu.get_idCard2("1111"))

# 调用方法来修改隐藏的属性
stu.set_idCard("1111","xxxxxxxxxxxxxxxxxx")

print(stu.get_idCard2("1111"))


# 总结:对于被隐藏的属性  访问和修改都需要通过方法 get用于获取  set用于设置(也称之为设置器和访问器)
封装
class A:

    def __f1(self):
        print("f1 run")

    def run_f1(self):
        self.__f1()

a = A()
a.run_f1()




# ATM 的取款功能
# 1.插入银行卡 2.输入密码 3.选择取款金额  4.取款

class ATM:
    def __insert_card(self):
        print("插入银行卡...")

    def __input_pwd(self):
        print("输入密码...")
    def __select_money(self):
        print("选择取款金额...")
    def withdraw(self):
        self.__insert_card()
        self.__input_pwd()
        self.__select_money()
        print("取款成功!....")

atm = ATM()

atm.withdraw() # 外部调用这个简单的接口 就能完成一系列复杂的操作

# atm.select_money()  #直接调用内部的方法 是没有意义的无法完成整个功能

# 当然用户按照流程一一调用也可以完成功能 但是太麻烦
# atm.insert_card()
# atm.input_pwd()
# atm.select_money()

# 封装方法 如何封装 给方法名字前面加上双下划线
# 封装方法的好处:
# 1.提高安全性
# 2.隔离复杂度  (将复杂的内容隔离到内部 外部只留下简单的接口 对于使用者 难度降低)
封装方法
class Person:

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

    def get_idCard(self):
        return self.__idCard

    def __test(self):
        pass

    print("aaaaaaaaaa")


# p = Person("比尔盖茨","男",20,"322323232332332")
#
# # print(p.__idCard)
# p.__idCard = "XXXXXX"
# print(p.__idCard)
#
# print(p.get_idCard())
#
# print(p.__dict__)
# print(Person.__dict__)
#
#
# p.__xxxxxxxxxxxx = 1
#
# print(p.__dict__)


# 通过__dict__ 可以发现
# 1.私有的属性和方法名称 前自动加上了_类名 python就是通过这样的转换方式来实现封装
# 2.只有在类的内部的双下划线才会被自动转换,并且这个转换过程只执行一次,在类定义完成后 后续添加的双下划线开头的名称是不会自动转换的
# 3.父类中私有的方法 子类中无法使用


#在父类中定义的私有方法 能不能被子类所覆盖?


# class A:
#     def __f1(self):
#         print("A __f1")
#
# class B(A):
#     def __f2(self):
#         # self.__f1()
#         super().__f1()
#         print("B __f2")
#
#     def test(self):
#         self.__f2()
#
# b = B()
# b.test()




# 子类无法覆盖父类的私有方法

# class  A:
#     def f(self):
#         self.__f1()   #_A__f1
#
#     def __f1(self):
#         print("A  __f1")
#
# class B(A):
#     def __f1(self):  # _B__f1
#         print("B __f1")
#
#     def f2(self):
#         self.f()
#
# b = B()
# b.f2()

# 之所以无法覆盖 是因为 子类和父类中的私有方法 名称必然不相同 所以无法覆盖   子类的方法一定子类独有的 因为名称不同




class A:
    __age = 10

# 绕过封装的限制直接访问  这是毫无意义的
print(A._A__age)
封装的实现原理

当一些属性的值 不是固定的而是通过计算得来的时候  我们必须为这个属性增加方法才能完成计算
但是一旦使用方法后 该属性的访问就变成了方法的调用 很明显与其他的属性访问方式不同,这样给使用者造成迷惑
所以需要将这个方法伪装成普通属性 这就用到了Property

property可以将方法伪装成属性 利用这个特点 我们也可以将其使用到封装中
之前没有这个装饰器我们需要为私有的属性 提供两个方法 但是这样一来方位私有属性时的方式就发生了变化
这时候就可以使用property来进行伪装 使得访问私有属性与访问普通属性的方式一致

另外 property还提供了 setter(用于修改属性的值) 和 deleter(删除属性的值)
# BIM案例:
class Person:
    def __init__(self,name,weight,height):
        self.name = name
        self.weight = weight
        self.height = height
        # self.bmi = weight/(height*height)
    # def bmi(self):
    #     return self.weight / (self.height * self.height)
    @property
    def bmi(self):
        return self.weight / (self.height * self.height)

p = Person("尔晴",50,1.5)
# print(p.bmi)
# p.weight = 90
# print(p.bmi)

# 现在 虽然可以实现需求 但是我们把一个属性变成了一个行为 这是不合理的
# print(p.bmi())
# p.weight = 90
# print(p.bmi())

# 使用property装饰器 可以将一个方法伪装成一个属性
print(p.bmi)
p.height += 0.2
print(p.bmi)


class Student:
    def __init__(self,name,sex,idCard):
        self.name = name
        self.sex = sex
        self.__idCard = idCard

    def get_idCard(self):
        return self.__idCard

    def set_idCard(self,new_id):
        self.__idCard = new_id

    @property # 需要掌握
    def idCard(self):
        return self.__idCard


    @idCard.setter #了解的
    def idCard(self,new_id):
        self.__idCard = new_id

    @idCard.deleter # 了解的
    def idCard(self):
        print("身份证属性被删除了.....")
        del self.__idCard


stu = Student("尔康","","323254554554")

# print(stu.get_idCard())

# stu.set_idCard("xxxx")

print(stu.get_idCard()) # 使用装饰器前
print(stu.name) # 普通属性的访问

print(stu.idCard) # 使用装饰器后

stu.idCard = "aaaaaaa" # 使用装饰器后的修改操作

print(stu.idCard)

del stu.idCard

print(stu.__dict__)
print(Student.__dict__)
peoperty
多态
什么是多态
多种状态 形态
生活中具备多种形态的事物 水(水蒸气,冰,液态水) 奥特曼(红色 力量,蓝色 速度) 数码宝贝(究极形态)
一种事物 具备多种形态或状态 就称之为多态
官方解释:不同对象 可以相应同一方法,并作出不同的行为,产生不同结果

如何实现多态?
让几个不同类拥有相同父类,这样一来他们就具备了相同的方法,每个子类要覆盖父类的方法,从而每个类的对象行为都不同

程序中的多态,例如动物

  
class Animal:
    def eat(self):
        print("动物在吃东西...")
    def sleep(self):
        print("动物在睡觉...")
    def drink(self):
        print("动物需要水.....")


class Person(Animal):
    def eat(self):
        print("人吃粮食...")

class Pig(Animal):
    def eat(self):
        print("猪吃饲料...")

class Dog(Animal):
    def eat(self):
        print("狗吃骨头...")


person = Person()
pig = Pig()
dog = Dog()

person.eat()
pig.eat()
dog.eat()

#假设你学习了C1驾照  意味着 所有C1类的汽车都能开  因为每种C1汽车的驾驶方式相同


# 当使用了多态之后 对象的使用者不需要关系这个对象具体的实现,只需要知道该对象属于哪个基类,就能直接使用它
# 如此扩展性变高了


class Phone:
    def call(self):
        print("手机就能打电话..")

    def send_msg(self):
        print("手机能发短信..")


class WindowsPhone(Phone):
    def call(self):
        print("拨号打电话..")

    def send_msg(self):
        print("输入号码发短信..")

class IPhone(Phone):
    def call(self):
        print("拨号打电话..")


    def send_msg(self):
        print("输入号码发短信..")




# 可以定义一个方法接受一个手机为参数 无论是是类型的手机 都可以被使用

def CALL(phone):
    phone.call()

wp = WindowsPhone()
ipx = IPhone()

CALL(wp)
CALL(ipx)


# 系统内置的方法有很多都体现了多态


print(len("abc"))
print(len([1,2,3,4,]))
print(len({"name":"123","sex":"man"}))

print("abc".__len__())
print([1,2,3,4,].__len__())
print({"name":"123","sex":"man"}.__len__())

print(len({1,23,4,5}))
"""
    多态是多个类的对象拥有相同的方法,但是我们没有从严格要求说必须提供这些方法,子类完全可以不提供这些方法
    # 现在要做的就是 严格要求 子类必须实现父类声明的方法

"""

import abc
# abstract class 是抽象类的缩写   抽象的意思是 不清晰 不具体 看不懂

#使用ABC模块来限制子类 的步骤
#1.为类中指定元类为abc.ABCMeta
#2.在相应的方法上加上abc.abstractmethod装饰器

class Animal(metaclass=abc.ABCMeta):

    @abc.abstractmethod
    def eat(self):
        pass

    @abc.abstractmethod
    def drink(self):
        pass


class Cat(Animal):
    def eat(self):
        print("猫爱吃鱼肉...")

    def drink(self):
        print("用舌头舔..")

class Dog(Animal):
    def eat(self):
        print("狗爱吃骨头...")
    def drink(self):
        print("用舌头舔..")

class Pig(Animal):
    def eat(self):
        print("猪 爱吃草...")

    def drink(self):
        print("用嘴吸的..")

p = Pig()
# p.eat()

c = Cat()
# c.eat()

# 多态的好处 完全不需要考虑得到的对象时声明类型 只要知道了其基类中的内容就能使用
def feeding(animal):
    animal.eat()
    animal.drink()

feeding(c)
feeding(p)
# 多态中的基类 相当于(协议 标准 规范) 要求子类必须满足这些标准
多态abc模块
class Duck:

    def bark(self):
        print("鸭子嘎嘎叫...")

    def run(self):
        print("摇摇晃晃走....")

class Chicken:
    def bark(self):
        print("鸡咯咯叫...")

    def run(self):
        print("摇摇晃晃走....")

def test(obj):
    obj.bark()
    obj.run()
duck = Duck()
c = Chicken()

test(duck)
test(c)

# 如果你足够自觉 你可以不使用abc模块 也不需要基类 自觉地将方法名字都写成一样 同样可以实现多态
# 这种方式称之为鸭子类型
鸭子类型





原文地址:https://www.cnblogs.com/BestSkye/p/10115740.html