Day-3

# review python by zzk
# From https://www.cnblogs.com/Eva-J/p/7277026.html
# and my point


import time
# for i in range(0,101,2):
#      time.sleep(0.1)
#      char_num = i//2      #打印多少个'*'
#      per_str = '\r%s%% : %s\n' % (i, '*' * char_num) if i == 100 else '\r%s%% : %s'%(i,'*'*char_num)
#      print(per_str,end='', flush=True)
#小越越  : \r 可以把光标移动到行首但不换行

# hash(o) o是参数,返回一个可hash变量的哈希值,不可hash的变量被hash之后会报错
# t = (1,2,3)
# l = [1,2,3]
# print(hash(t))  #可hash
# print(hash(l))  #会报错
#
# '''
# 结果:
# TypeError: unhashable type: 'list'
# '''
# hash函数会根据一个内部的算法对当前可hash变量进行处理,返回一个int数字。
# *每一次执行程序,内容相同的变量hash值在这一次执行过程中不会发生改变。


# filter
#
# filter()函数接收一个函数 f 和一个list,这个函数 f 的作用是对每个元素进行判断,返回 True或 False,
# filter()根据判断结果自动过滤掉不符合条件的元素,返回由符合条件元素组成的新list。

# 要从一个list [1, 4, 6, 7, 9, 12, 17]中删除偶数,保留奇数,首先,要编写一个判断奇数的函数:
def is_odd(x):
    return x % 2 == 1
odd=list(filter(is_odd, [1, 4, 6, 7, 9, 12, 17]))
# print(odd)

# 删除 None 或者空字符串:
def is_not_empty(s):
    return s and len(s.strip()) > 0
not_empty=list(filter(is_not_empty, ['test', None, '', 'str', '  ', 'END']))
# print(not_empty)
# 注意: s.strip(rm) 删除 s 字符串中开头、结尾处的 rm 序列的字符。

# 利用filter()过滤出1~100中平方根是整数的数
import math
def is_sqr(x):
    return math.sqrt(x) % 1 == 0
# print(list(filter(is_sqr, range(1, 101))))
# [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]


# map
# map函数应用于每一个可迭代的项,返回的是一个结果list。
# 如果有其他的可迭代参数传进来,map函数则会把每一个参数都以相应的处理函数进行迭代处理。
# map()函数接收两个参数,一个是函数,一个是序列,map将传入的函数依次作用到序列的每个元素,并把结果作为新的list返回

# 有一个list, L = [1,2,3,4,5,6,7,8],我们要将f(x)=x^2作用于这个list上,那么我们可以使用map函数处理
# L = [1,2,3,4,5,6,7,8]
# def pow2(x):
#     return x*x
#
# m=list(map(pow2,L))
# print(m)
#
# mm=[ i*i for i in range(1,9)]
# print(mm)


# 内置函数——sorted
# 对List、Dict进行排序,Python提供了两个方法
# 对给定的List L进行排序,
# 方法1.用List的成员函数sort进行排序,在本地进行排序,不返回副本
# 方法2.用built-in函数sorted进行排序(从2.4开始),返回副本,原始输入不变

# 参数说明:
# iterable:是可迭代类型;
# key:传入一个函数名,函数的参数是可迭代类型中的每一项,根据函数的返回值大小排序;
# reverse:排序规则. reverse = True  降序 或者 reverse = False 升序,有默认值。
# 返回值:有序列表

# l1 = [1,3,5,-2,-4,-6]
# l2 = sorted(l1,key=abs)
# print(l1)
# print(l2)

# 匿名函数
# 匿名函数:为了解决那些功能很简单的需求而设计的一句话函数
# 这段代码
def calc(n):
    return n ** n
# print(calc(10))

# 换成匿名函数
calc = lambda n: n ** n     # lambda 为定义匿名函数的关键字,n为函数参数,:后面的n*n为这个函数的返回值
# print(calc(10))

# 函数名 = lambda 参数 :返回值

#参数可以有多个,用逗号隔开
#匿名函数不管逻辑多复杂,只能写一行,且逻辑执行结束后的内容就是返回值
#返回值和正常的函数一样可以是任意数据类型

def add(x,y):
    return x+y
x,y = 3,4
lad = lambda x,y:x+y
# print(lad)


# 递归的定义——在一个函数里再调用这个函数本身
# 简单版二分法
# l = [2,3,5,10,15,16,18,22,26,30,32,35,41,42,43,55,56,66,67,69,72,76,82,83,88]
#
# def func(l,aim):
#     mid = (len(l)-1)//2     # // 为取整除 - 向下取接近除数的整数  9//2=4
#     print(mid)
#     if l:
#         if aim > l[mid]:
#             func(l[mid+1:],aim)
#         elif aim < l[mid]:
#             func(l[:mid],aim)
#         elif aim == l[mid]:
#             print('---- {} ----'.format(l[mid]))
#             print("bingo",mid)
#     else:
#         print('找不到')
# func(l,67)
# func(l,88)
# 12
# 5
# ---- 67 ----
# bingo 5
# 12
# 5
# 2
# 1
# 0
# ---- 88 ----
# bingo 0


# Json模块
# 提供了四个功能:dumps、dump、loads、load

# json.dumps(): 对数据进行编码。
# json.loads(): 对数据进行解码。


import json
dic = {'k1':'v1','k2':'v2','k3':'v3'}
str_dic = json.dumps(dic)       # 序列化:将一个字典转换成一个字符串
# print(type(str_dic),str_dic)  # <class 'str'> {"k3": "v3", "k1": "v1", "k2": "v2"}
#注意,json转换完的字符串类型的字典中的字符串是由""表示的

dic2 = json.loads(str_dic)  #反序列化:将一个字符串格式的字典转换成一个字典
#注意,要用json的loads功能处理的字符串类型的字典中的字符串必须由""表示
# print(type(dic2),dic2)  #<class 'dict'> {'k1': 'v1', 'k2': 'v2', 'k3': 'v3'}

list_dic = [1,['a','b','c'],3,{'k1':'v1','k2':'v2'}]
str_dic = json.dumps(list_dic)      # 也可以处理嵌套的数据类型
# print(type(str_dic),str_dic)        # <class 'str'> [1, ["a", "b", "c"], 3, {"k1": "v1", "k2": "v2"}]
list_dic2 = json.loads(str_dic)
# print(type(list_dic2),list_dic2)        # <class 'list'> [1, ['a', 'b', 'c'], 3, {'k1': 'v1', 'k2': 'v2'}]

# import json
# f = open('json_file','w')
# dic = {'k1':'v1','k2':'v2','k3':'v3'}
# json.dump(dic,f)  #dump方法接收一个文件句柄,直接将字典转换成json字符串写入文件
# f.close()
#
# f = open('json_file')
# dic2 = json.load(f)  #load方法接收一个文件句柄,直接将文件中的json字符串转换成数据结构返回
# f.close()
# print(type(dic2),dic2)

# 从上面的操作可以看出,json.dumps() json.dump()的区别
# dumps是将dict转化成str格式,loads是将str转化成dict格式。
# dump和load也是类似的功能,只是与文件操作结合起来了。
# 注:实际中dump用的较少。

# json是一种所有的语言都可以识别的数据结构。
# 如果我们将一个字典或者序列化成了一个json存在文件里,那么java代码或者js代码也可以拿来用


# 面相对象-面向过程
# 面向过程的程序设计的核心是过程(流水线式思维),过程即解决问题的步骤,
# 面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西。
# 优点是:极大的降低了写程序的复杂度,只需要顺着要执行的步骤,堆叠代码即可。
# 缺点是:一套流水线或者流程就是用来解决一个问题,代码牵一发而动全身。
# 应用场景:一旦完成基本很少改变的场景,著名的例子有Linux內核,git,以及Apache HTTP Server等。

# 面向对象的程序设计的
# 优点是:解决了程序的扩展性。对某一个对象单独修改,会立刻反映到整个体系中。
# 缺点:可控性差,无法向面向过程的程序设计流水线式的可以很精准的预测问题的处理流程与结果,
# 面向对象的程序一旦开始就由对象之间的交互解决问题,即便是上帝也无法预测最终结果。
# 应用场景:需求经常变化的软件,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方。

# python中一切皆为对象,类型的本质就是类,所以,不管你信不信,你已经使用了很长时间的类了

class Person:  # 定义一个人类
    role = 'person'  # 人的角色属性都是人

    def __init__(self, name):
        self.name = name  # 每一个角色都有自己的昵称;

    def walk(self):  # 人都可以走路,也就是有一个走路方法
        print("person is walking...")

# print(Person.role)  # 查看人的role属性
# print(Person.walk)  # 引用人的走路方法,注意,这里不是在调用

# 类有两种作用:属性引用和实例化

# 属性引用(类名.属性)
# 实例化:类名加括号就是实例化,会自动触发__init__函数的运行,可以用它来为每个实例定制自己的特征
# 实例化的过程就是类——>对象的过程
# 原本我们只有一个Person类,在这个过程中,产生了一个egg对象,有自己具体的名字、攻击力和生命值。
# 语法:对象名 = 类名(参数)

egg = Person('egon')  #类名()就等于在执行Person.__init__()
#执行完__init__()就会返回一个对象。这个对象类似一个字典,存着属于这个人本身的一些属性和方法。
# 查看属性&调用方法
# print(egg.name)     #查看属性直接 对象名.属性名
# print(egg.walk())   #调用方法,对象名.方法名()


# self:在实例化时自动将对象/实例本身传给__init__的第一个参数
# 你也可以给他起个别的名字,但是正常人都不会这么做。
# 因为你瞎改别人就不认识

# 一:我们定义的类的属性到底存到哪里了?有两种方式查看
# dir(类名):查出的是一个名字列表
# 类名.__dict__:查出的是一个字典,key为属性名,value为属性值
#
# 二:特殊的类属性
# 类名.__name__# 类的名字(字符串)
# 类名.__doc__# 类的文档字符串
# 类名.__base__# 类的第一个父类(在讲继承时会讲)
# 类名.__bases__# 类所有父类构成的元组(在讲继承时会讲)
# 类名.__dict__# 类的字典属性
# 类名.__module__# 类定义所在的模块
# 类名.__class__# 实例对应的类(仅新式类中)


# 面向对象小结——定义及调用的固定模式
# class 类名:
#     def __init__(self,参数1,参数2):
#         self.对象的属性1 = 参数1
#         self.对象的属性2 = 参数2
#
#     def 方法名(self):pass
#
#     def 方法名2(self):pass
#
# 对象名 = 类名(1,2)  #对象就是实例,代表一个具体的东西
#                   # 类名() : 类名+括号就是实例化一个类,相当于调用了__init__方法
#                   # 括号里传参数,参数不需要传self,其他与init中的形参一一对应
#                   # 结果返回一个对象
# 对象名.对象的属性1   #查看对象的属性,直接用 对象名.属性名 即可
# 对象名.方法名()     #调用类中的方法,直接用 对象名.方法名() 即可

from math import pi

class Circle:
    """
    定义了一个圆形类;
    提供计算面积(area)和周长(perimeter)的方法
    """
    def __init__(self,radius):
        self.radius = radius

    def area(self):
         return pi * self.radius * self.radius

    def perimeter(self):
        return 2 * pi *self.radius

circle =  Circle(10)        #实例化一个圆
area1 = circle.area()       #计算圆面积
per1 = circle.perimeter()       #计算圆周长
# print(area1,per1)       #打印圆面积和周长


# 类命名空间与对象、实例的命名空间
# 创建一个类就会创建一个类的名称空间,用来存储类中定义的所有名字,这些名字称为类的属性
#
# 而类有两种属性:静态属性和动态属性
#
# 静态属性就是直接在类中定义的变量
# 动态属性就是定义在类中的方法

# 创建一个对象/实例就会创建一个对象/实例的名称空间,存放对象/实例的名字,称为对象/实例的属性


class Weapon:
    def prick(self, obj):  # 这是该装备的主动技能,扎死对方
        obj.life_value -= 500  # 假设攻击力是500


class Person:  # 定义一个人类
    role = 'person'  # 人的角色属性都是人

    def __init__(self, name):
        self.name = name  # 每一个角色都有自己的昵称;
        self.weapon = Weapon()  # 给角色绑定一个武器;


# egg = Person('egon')
# egg.weapon.prick()
# egg组合了一个武器的对象,可以直接egg.weapon来使用组合类中的所有方法


# radius 半径
from math import pi

class Circle:
    '''
    定义了一个圆形类;
    提供计算面积(area)和周长(perimeter)的方法
    '''
    def __init__(self,radius):
        self.radius = radius

    def area(self):
         return pi * self.radius * self.radius

    def perimeter(self):
        return 2 * pi *self.radius


# circle =  Circle(10) #实例化一个圆
# area1 = circle.area() #计算圆面积
# per1 = circle.perimeter() #计算圆周长
# print(area1,per1) #打印圆面积和周长

class Ring:
    '''
    定义了一个圆环类
    提供圆环的面积和周长的方法
    '''
    def __init__(self,radius_outside,radius_inside):
        self.outsid_circle = Circle(radius_outside)
        self.inside_circle = Circle(radius_inside)

    def area(self):
        return self.outsid_circle.area() - self.inside_circle.area()

    def perimeter(self):
        return  self.outsid_circle.perimeter() + self.inside_circle.perimeter()


# ring = Ring(10,5) #实例化一个环形
# print(ring.perimeter()) #计算环形的周长
# print(ring.area()) #计算环形的面积


class BirthDate:
    def __init__(self,year,month,day):
        self.year=year
        self.month=month
        self.day=day

class Couse:
    def __init__(self,name,price,period):
        self.name=name
        self.price=price
        self.period=period

class Teacher:
    def __init__(self,name,gender,birth,course):
        self.name=name
        self.gender=gender
        self.birth=birth
        self.course=course

    def teach(self):
        print('teaching')

p1=Teacher('egon','male',
            BirthDate('1995','1','27'),     # 在这里初始化了BirthDate这个类,这就是组合!!!
            Couse('python','28000','4 months')      # 这里Couse的初始化
           )

# print(p1.birth.year,p1.birth.month,p1.birth.day)

# print(p1.course.name,p1.course.price,p1.course.period)
''' 
运行结果: 
1 27 
python 28000 4 months 
'''

# 面向对象的三大特性
# 什么是继承
# 继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类
class ParentClass1: #定义父类
    pass
class ParentClass2: #定义父类
    pass
class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass
    pass
class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类
    pass

# 查看继承
# >>> SubClass1.__bases__ #__base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类
# (<class '__main__.ParentClass1'>,)
# >>> SubClass2.__bases__
# (<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)

# 提示:如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,
# 它提供了一些常见方法(如__str__)的实现。
# >>> ParentClass1.__bases__
# (<class 'object'>,)
# >>> ParentClass2.__bases__
# (<class 'object'>,)

# 继承:是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构
# 在开发程序的过程中,如果我们定义了一个类A,然后又想新建立另外一个类B,但是类B的大部分内容与类A的相同时
#
# 我们不可能从头开始写一个类B,这就用到了类的继承的概念。
#
# 通过继承的方式新建类B,让B继承A,B会‘遗传’A的所有属性(数据属性和函数属性),实现代码重用


# 派生
# 子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类)
# 需要注意的是,一旦重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为准了
class Animal:
    '''
    人和狗都是动物,所以创造一个Animal基类
    '''
    def __init__(self, name, aggressivity, life_value):
        self.name = name  # 人和狗都有自己的昵称;
        self.aggressivity = aggressivity  # 人和狗都有自己的攻击力;
        self.life_value = life_value  # 人和狗都有自己的生命值;

    def eat(self):
        print('%s is eating'%self.name)

class Dog(Animal):
    '''
    狗类,继承Animal类
    '''
    def bite(self, people):
        '''
        派生:狗有咬人的技能
        :param people:
        '''
        people.life_value -= self.aggressivity

class Person(Animal):
    '''
    人类,继承Animal
    '''
    def attack(self, dog):
        '''
        派生:人有攻击的技能
        :param dog:
        '''
        dog.life_value -= self.aggressivity

# egg = Person('egon',10,1000)
# ha2 = Dog('二愣子',50,1000)
# print(ha2.life_value)
# print(egg.attack(ha2))
# print(ha2.life_value)

# 注意:像ha2.life_value之类的属性引用,会先从实例中找life_value然后去类中找,然后再去父类中找...直到最顶级的父类。
#
# 在子类中,新建的重名的函数属性,在编辑函数内功能的时候,有可能需要重用父类中重名的那个函数功能,应该是用调用普通函数的方式,
# 即:类名.func(),此时就与调用普通函数无异了,因此即便是self参数也要为其传值.
#
# 在python3中,子类执行父类的方法也可以直接用super方法.
class A:
    def hahaha(self):
        print('A')

class B(A):
    def hahaha(self):
        super().hahaha()
        #super(B,self).hahaha()
        #A.hahaha(self)
        print('B')

# a = A()
# b = B()
# b.hahaha()
# super(B,b).hahaha()


# 接口类
# 继承有两种用途:
# 一:继承基类的方法,并且做出自己的改变或者扩展(代码重用)
# 二:声明某个子类兼容于某基类,定义一个接口类Interface,
# 接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能
class Alipay:
    """
    支付宝支付
    """
    def pay(self,money):
        print('支付宝支付了%s元'%money)

class Applepay:
    """
    apple pay支付
    """
    def pay(self,money):        # 上面两个类都实现了pay这个方法
        print('apple pay支付了%s元'%money)


def pay(payment,money):
    """
    支付函数,总体负责支付
    :param payment: 对应支付的对象
    :param money: 要支付的金额
    :return:
    """
    payment.pay(money)


p = Alipay()
# pay(p,200)


# 开发中容易出现的问题
# class Alipay:
#     '''
#     支付宝支付
#     '''
#     def pay(self,money):
#         print('支付宝支付了%s元'%money)
#
# class Applepay:
#     '''
#     apple pay支付
#     '''
#     def pay(self,money):
#         print('apple pay支付了%s元'%money)
#
# class Wechatpay:
#     def fuqian(self,money):
#         '''
#         实现了pay的功能,但是名字不一样
#         '''
#         print('微信支付了%s元'%money)
#
# def pay(payment,money):
#     '''
#     支付函数,总体负责支付
#     对应支付的对象和要支付的金额
#     '''
#     payment.pay(money)        # 因为wechat这个类没有pay方法
#
#
# p = Wechatpay()
# pay(p,200)   #执行会报错

# 实践中,继承的第一种含义意义并不很大,甚至常常是有害的。因为它使得子类与基类出现强耦合。
# 继承的第二种含义非常重要。它又叫“接口继承”。
# 接口继承实质上是要求“做出一个良好的抽象,这个抽象规定了一个兼容接口,
# 使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的所有对象”——这在程序设计上,叫做归一化。

# 接口提取了一群类共同的函数,可以把接口当做一个函数的集合。
#
# 然后让子类去实现接口中的函数。
#
# 这么做的意义在于归一化,什么叫归一化,就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。
#
# 归一化,让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。
#
# 比如:我们定义一个动物接口,接口里定义了有跑、吃、呼吸等接口函数,
# 这样老鼠的类去实现了该接口,松鼠的类也去实现了该接口,由二者分别产生一只老鼠和一只松鼠送到你面前,
# 即便是你分别不到底哪只是什么鼠你肯定知道他俩都会跑,都会吃,都能呼吸。
#
# 再比如:我们有一个汽车接口,里面定义了汽车所有的功能,然后由本田汽车的类,奥迪汽车的类,大众汽车的类,他们都实现了汽车接口,
# 这样就好办了,大家只需要学会了怎么开汽车,那么无论是本田,还是奥迪,还是大众我们都会开了,
# 开的时候根本无需关心我开的是哪一类车,操作手法(函数调用)都一样

# 继承小结
# 继承的作用
# 减少代码的重用
# 提高代码可读性
# 规范编程模式

# 几个名词
# 抽象:抽象即抽取类似或者说比较像的部分。是一个从具题到抽象的过程。
# 继承:子类继承了父类的方法和属性
# 派生:子类在父类方法和属性的基础上产生了新的方法和属性
# 抽象类与接口类

# 1.多继承问题
# 在继承抽象类的过程中,我们应该尽量避免多继承;
# 而在继承接口的时候,我们反而鼓励你来多继承接口
#
# 2.方法的实现
# 在抽象类中,我们可以对一些抽象方法做出基础实现;
# 而在接口类中,任何方法都只是一种规范,具体的功能需要子类实现

# 钻石继承
# 新式类:广度优先
# 经典类:深度优先


# 多态性
# 一 什么是多态动态绑定(在继承的背景下使用时,有时也称为多态性)
#
# 多态性是指在不考虑实例类型的情况下使用实例
#
# 在面向对象方法中一般是这样表述多态性:
# 向不同的对象发送同一条消息(!!!obj.func():是调用了obj的方法func,又称为向obj发送了一条消息func)
# 不同的对象在接收时会产生不同的行为(即方法)。
# 也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。
#
# 比如:老师.下课铃响了(),学生.下课铃响了(),老师执行的是下班操作,学生执行的是放学操作,虽然二者消息一样,但是执行的效果不同



# 封装
# 隐藏对象的属性和实现细节,仅对外提供公共访问方式。
#
# 【好处】
# 1. 将变化隔离;
# 2. 便于使用;
# 3. 提高复用性;
# 4. 提高安全性;
#
# 【封装原则】
#  1. 将不需要对外提供的内容都隐藏起来;
#  2. 把属性都隐藏,提供公共方法对其访问。

# 私有变量和私有方法
# 在python中用双下划线开头的方式将属性隐藏起来(设置成私有的)

# 私有方法
# 3.在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的


# 封装与扩展性
# 封装在于明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者的代码;
# 而外部使用用者只知道一个接口(函数),只要接口(函数)名、参数不变,使用者的代码永远无需改变。
# 这就提供一个良好的合作基础——或者说,只要接口这个基础约定不变,则代码改变不足为虑。
# #类的设计者
# class Room:
#     def __init__(self,name,owner,width,length,high):
#         self.name=name
#         self.owner=owner
#         self.__width=width
#         self.__length=length
#         self.__high=high
#     def tell_area(self): #对外提供的接口,隐藏了内部的实现细节,此时我们想求的是面积
#         return self.__width * self.__length
#
#
# #使用者
# >>> r1=Room('卧室','egon',20,20,20)
# >>> r1.tell_area() #使用者调用接口tell_area
#
#
# #类的设计者,轻松的扩展了功能,而类的使用者完全不需要改变自己的代码
# class Room:
#     def __init__(self,name,owner,width,length,high):
#         self.name=name
#         self.owner=owner
#         self.__width=width
#         self.__length=length
#         self.__high=high
#     def tell_area(self): #对外提供的接口,隐藏内部实现,此时我们想求的是体积,内部逻辑变了,只需求修该下列一行就可以很简答的实现,而且外部调用感知不到,仍然使用该方法,但是功能已经变了
#         return self.__width * self.__length * self.__high
#
#
# #对于仍然在使用tell_area接口的人来说,根本无需改动自己的代码,就可以用上新功能
# >>> r1.tell_area()



# property属性
# property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值
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('zk',100,1.78)
# print(p1.bmi)       # 初始化之后,调用 @property修饰的功能函数,没有执行函数,但是会直接执行,然后返回值

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)  # 同上
# #注意:此时的特性area和perimeter不能被赋值
# c.area=3 #为特性area赋值
'''
抛出异常:
AttributeError: can't set attribute
'''

# 为什么要用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
#     def name(self):
#         raise TypeError('Can not delete')
#
# f=Foo('egon')
# print(f.name)
# # f.name=10 #抛出异常'TypeError: 10 must be str'
# del f.name #抛出异常'TypeError: Can not delete'


# 真正的封装是,经过深入的思考,做出良好的抽象,给出“完整且最小”的接口,并使得内部细节可以对外透明
# (注意:对外透明的意思是,外部调用者可以顺利的得到自己想要的任何功能,完全意识不到内部细节的存在)

# classmethod
# classmethod 修饰符对应的函数不需要实例化,不需要 self 参数,
# 但第一个参数需要是 表示自身类的 cls 参数,可以来调用类的属性,类的方法,实例化对象等
# class A(object):
#     bar = 1
#
#     def func1(self):
#         print('foo')
#
#     @classmethod
#     def func2(cls):
#         print('func2')
#         print(cls.bar)      # 这里调用了bar方法,利用的是代表自身类的cls
#         cls().func1()  # 调用 foo 方法
#
# A.func2()  # 不需要实例化


class A(object):
    # 属性默认为类属性(可以给直接被类本身调用)
    num = "类属性"

    # 实例化方法(必须实例化类之后才能被调用)
    def func1(self):  # self : 表示实例化类后的地址id
        print("func1")
        print(self)

    # 类方法(不需要实例化类就可以被类本身调用)
    @classmethod
    def func2(cls):  # cls : 表示没用被实例化的类本身
        print("func2")
        print(cls)
        print(cls.num)
        cls().func1()

    # # 不传递传递默认self参数的方法(该方法也是可以直接被类调用的,但是这样做不标准)
    # def func3():
    #     print("func3")
    #     print(A.num)  # 属性是可以直接用类本身调用的


# A.func1() 这样调用是会报错:因为func1()调用时需要默认传递实例化类后的地址id参数,如果不实例化类是无法调用的
A.func2()
# A.func3()
# result:
# func2
# <class '__main__.A'>
# 类属性
# func1
# <__main__.A object at 0x10806dcc0>


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值