python----面向对象(绑定/继承与派生/组合/抽象类/封装/property/反射..)

编程范式

实现一个任务的方式偶很多种,不同的方式,对这些不同的编程方式的特点进行归纳总结出来的边恒方式类别称为编程范式.

最重要的编程范式有两种:

1.面向过程编程:核心是过程二字,过程指的是解决问题的步骤,设计一条流水线,机械式的思维方式

  优点:复杂的问题流程化,进而简单化

  缺点:可扩展性差

  应用场景:面向过程的程序设计思想一般用于那些功能一旦实现之后就很少需要改变的场景.

def intercactive():
    username = input('>>:').strip()
    password = input('>>').strip()
    email = input('>>').strip()
    return {
        'username': username,
        'password': password,
        'email': email
    }


def check(user_info):
    flag = True

    if len(user_info['username']) == 0:
        print('用户名不能为空')
        flag = False
    if len(user_info['password']) < 6:
        print('密码不能少于6位')
        flag = False
    if not re.search(r'@.*?\.com$', user_info['email']):
        print('邮箱不合法')
        flag = False
    return {
        'flag': flag,
        'user_info': user_info
    }


def register(check_info):
    if check_info['flag']:
        with open('db.json', 'w', encoding='utf-8')as f:
            json.dump(check_info['user_info'], f)


def main():
    user_info = intercactive()
    check_info = check(user_info)
    register(check_info)


if __name__ == '__main__':
    main()
View Code

2.面向对象编程:核心是对象二字,对象就是特征与技能的结合体

  优点:可扩展性强

  缺点:编程复杂度高

  应用场景:用户需求经常变化,互联网应用,游戏,企业内部应用

  强调:站在不同角度,得到的分类不同

  现实生活中:一定先有对象,后有种类

  编程中:一定先定义类,后调用类来产生对象

名词解释

类:一个类即是对同一类拥有相同属性的对象的抽象,蓝图,原型,模板.在类中定义了这些对象的都具备的属性,共同的方法.

属性:人类包括很多特征,把这些特征用程序来描述的话,叫做属性,比如年龄,身高,性别,姓名等都叫属性,一个类中,可以有多个属性.

方法:人类不止有身高,年龄,性别这些属性,还能做好多事情,比如:说话,走路.吃饭等.相比较于属性是名词,说话,走路时动词.这些动词用程序来描述叫做方法.

实例(对象):一个对象即是一个类的实例化后实例,一个类必须经过实例化后方可在程序中调用,一个类可以实例化多个对象,每个对象亦可以有不同的属性,就像人类是指所有人,每个人是指具体的对象,人与人之前有共性,亦有不同.

实例化:把一个类转变为一个对象的过程叫做实例化.

类的使用

class LuffyStudent:
    shool = 'luffycitry'  # 数据属性

    def learn(self):  # 函数属性
        print('is learning')

    def eat(self):  # 函数属性
        print('is eating')

    def sleep(self):
        print('is sleeping')


# 查看类的名称空间
print(LuffyStudent.__dict__)
# print(LuffyStudent.__dict__['shool'])
# print(LuffyStudent.__dict__['eat'])

# 查看
# print(LuffyStudent.shool)  # print(LuffyStudent.__dict__['shool'])
# print(LuffyStudent.learn)  # print(LuffyStudent.__dict__['eat'])

# 增加
# LuffyStudent.county = 'China'
# print(LuffyStudent.__dict__)
# print(LuffyStudent.county)

# 删除
# del LuffyStudent.county
# print(LuffyStudent.__dict__)

# 修改
# LuffyStudent.shool = '社会'
# print(LuffyStudent.__dict__)
View Code
  • 类中可以有任意python代码,这些代码在类定义阶段便会执行,因而会产生新的名称空间,用来存放类的变量名与函数名,可以通过LuffyStudent.__dict__查看
  • 类中定义的名字,都是类的属性,点是访问属性的语法。

类的方法

构造方法:

__init__(...)被称为 构造方法或初始化方法,在例实例化过程中自动执行,目的是初始化实例的一些属性。每个实例通过__init__初始化的属性都是独有的.

刚才定义的这个类体现了面向对象的第一个基本特性,封装,其实就是使用构造方法将内容封装到某个具体对象中,然后通过对象直接或者self间接获取被封装的内容.

普通方法:

定义类的一些正常功能,比如人这个类, 可以说话、走路、吃饭等,每个方法其实想当于一个功能或动作

__del___实例在内存中被删除时,会自动执行这个方法.

class Open:
    def __init__(self, filename):
        print('open file......')
        self.filename = filename

    def __del__(self):
        print('回收操作系统资源:self.close')


f = Open('settings.py')

# del f  # f.__del__()
print('------mian------')  # del f  # f.__del__()
View Code

属性查找与绑定方法

类有两种属性:

1.数据属性:类的数据属性是所有对象共享的,id都一样

print(LuffyStudent.shool, id(LuffyStudent.shool)) 
print(stu1.shool, id(stu1.shool))
print(stu2.shool, id(stu2.shool))
print(stu3.shool, id(stu3.shool))

打印结果:
luffycitry 3771432
luffycitry 3771432
luffycitry 3771432
luffycitry 3771432
View Code

2.函数属性:类的函数属性是绑定给对象用的,称为绑定到对象的方法,id都不一样

<function LuffyStudent.learn at 0x0061C9C0>
<bound method LuffyStudent.learn of <__main__.LuffyStudent object at 0x0060FCB0>>
<bound method LuffyStudent.learn of <__main__.LuffyStudent object at 0x0060FCD0>>
<bound method LuffyStudent.learn of <__main__.LuffyStudent object at 0x0060FD30>>
View Code

绑定方法:

  • 类中定义的函数(没有被任何装饰器装饰的)是类的函数属性,类可以使用,但必须遵循函数的参数规则,有几个参数需要传几个参数.
  • 类中定义的函数,主要是对象使用的,而且是绑定到对象的,虽然所有对象指向的都是相同的功能,但是绑定到不同的对象就是不同的绑定方法.
  • 强调:绑定到对象的方法的特殊之处在于,绑定给谁就由谁来调用,谁来调用就会将谁本身当做第一个参数传给方法,即自动传值(方法__init__也是一样的道理)
class Student:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def learn(self):
        print('%s is learning %s' % (self.name, self.age))

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

    def sleep(self):
        print('%s is sleeping' % self.name)


s1 = Student('王一', 18)
s2 = Student('王二', 18)

s1.learn()
s1.eat()
View Code

继承与派生

继承:

类与类之间的关系,是一种什么是什么的关系.

继承的功能之一就是用来解决代码重用的问题,继承是一种创建新类的方式.

新建的类可以继承一个或多个父类,父类又可以成为基类或超类,新建的类称为派生类或子类.

多继承与单继承:

class Parentclass:
    pass


class Parentclass1:
    pass


class Subclass(Parentclass):
    pass


class Subclass1(Parentclass, Parentclass1):
    pass


print(Subclass.__bases__)  # __bases__查看继承的类
print(Subclass1.__bases__)

打印结果:
(<class '__main__.Parentclass'>,)
(<class '__main__.Parentclass'>, <class '__main__.Parentclass1'>)
View Code

继承与抽象:

抽象及抽取比较相似的地方.

抽象分两个层次:

1.将奥巴马和梅西这俩对象比较像的部分抽取成类;

2.将人,猪,狗这三个类比较像的部分抽取成父类。

继承是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。

抽象只是分析和设计的过程中,一个动作或者说一种技巧,通过抽象可以得到类

 

继承与重用性:

在开发过程中,我新建的类中,很多功能都是之前类中定义好的,我们在新建类的时候不可能再重新写一遍,这时我们可以继承之前的类,例子如下:

class Hero:
    x = 3

    def __init__(self, nickname, life_value, aggressivity):
        self.nickname = nickname
        self.life_value = life_value
        self.aggressivity = aggressivity

    def attack(self, enemy):
        enemy.life_value -= self.aggressivity


class Garen(Hero):
    x = 5
    pass


class Riven(Hero):
    pass


g1 = Garen('ike', 100, 30)  # 实例化一个对象
l1 = Riven('joker', 90, 20)  # 实例化一个对象
print(l1.life_value)  # 打印l1的生命值
g1.attack(l1)  # 调用父类的方法
print(l1.life_value)  # 打印l1的值
print(g1.nickname, g1.life_value, g1.aggressivity) 
View Code

属性查找:

class Foo:
    def f1(self):
        print('from Foo.f1')

    def f2(self):
        print('from Foo.f2')
        self.f1()  # b.F1()  先从自己找,自己没有去自己的类找,没有然后去自己的父类找


class Bar(Foo):
    def f1(self):
        print('from Bar.f2')


b = Bar()
b.f2()
View Code

经典类与新式类:

1.只有在python2中才分新式类和经典类,python3中统一都是新式类
2.在python2中,没有显式的继承object类的类,以及该类的子类,都是经典类
3.在python2中,显式地声明继承object的类,以及该类的子类,都是新式类
4.在python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式类
# 1.新式类

# 2.经典类

# 在 python2中-->经典类:没有继承object的类,以及他的子类,都称之为经典类

class Foo:
    pass


class Bar(Foo):
    pass


# 在 python2中-->新式类:继承object的类,以及他的子类,都称之为新式类

class Foo(object):
    pass


class Bar(Foo):
    pass


# 在python3中-->新式类:一个类没有继承object的类,默认都继承object类
class Foo:
    pass


print(Foo.__bases__)
View Code

派生:

子类继承父类,子类可以添加自己的属性或者在自己这里重新定义这些属性,不会影响父类,注意的是,一旦重新定义了自己的属性且与父类重名,调用的时候,就以子类为准.

在子类中,新建的重名的函数属性,在编辑函数内功能的时候,有可能需要重用父类中重名的那个函数功能,应该是用调用普通函数的方式,即:类名.func(),此时就与调用普通函数无异了,因此即便是self参数也要为其传值.

class Hero:
    x = 3

    def __init__(self, nickname, life_value, aggressivity):
        self.nickname = nickname
        self.life_value = life_value
        self.aggressivity = aggressivity

    def attack(self, enemy):
        enemy.life_value -= self.aggressivity
        print('from Hero class')


class Garen(Hero):
    camp = 'Demacia'

    def attack(self, enemy):
        Hero.attack(self, enemy)
        print('from Garen class')


class Riven(Hero):
    camp = 'Noxus'


g = Garen('草丛伦', 100, 30)
r = Riven('瑞雯', 90, 20)
print(g.camp)  # 自己(Garen('草丛伦', 100, 30))没有,去自己的类找.
g.attack(r)
print(r.life_value)
View Code

继承的实现原理:

python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表,例如:

class A:
    def test(self):
        print('from A')
    # pass


class B(A):
    def test(self):
        print('from B')
        # pass


class C(A):
    def test(self):
        print('from C')
    # pass


class D(B):
    def test(self):
        print('from D')
    # pass


class E(C):
    def test(self):
        print('from E')
    # pass


class F(D, E):
    def test(self):
        print('from F')
    # pass


# F-D-B-E-C-A
# [<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
print(F.mro()) 
View Code

在Java和C#中子类只能继承一个父类,而Python中子类可以同时继承多个父类,如果继承了多个父类,那么属性的查找方式有两种,分别是:深度优先和广度优先

深度优先:根据多继承继承一条线查底,一般用于经典类.

广度优先:根据多继承继承,查到根节点的上一个节点,然后从第二个继承再继续往下查,以此类推.一般用于新式类.

经典类,多继承的情况下,会按照深度优先的方式查找下去,一条线插到底.

 

新式类,多继承的情况下,会按照广度优先的方式查下去.

class A:
    def test(self):
        print('from A')
    # pass


class B(A):
    def test(self):
        print('from B')
        # pass


class C(A):
    def test(self):
        print('from C')
    # pass


class D(B):
    def test(self):
        print('from D')
    # pass


class E(C):
    def test(self):
        print('from E')
    # pass


class F(D, E):
    def test(self):
        print('from F')
    # pass


# F-D-B-E-C-A
print(F.mro())

f = F()
f.test()
View Code

在子类中调动父类的方法:

在子类派生出的新方法中,往往需要重用父类的方法,我们有两种实现方式:

方式一:

指名道姓,即父类.父类方法()    不依赖继承

class Hero:

    def __init__(self, nickname, life_value, aggressivity):
        self.nickname = nickname
        self.life_value = life_value
        self.aggressivity = aggressivity

    def attack(self, enemy):
        enemy.life_value -= self.aggressivity


class Garen(Hero):
    camp = 'Demacia'

    def attack(self, enemy):
        Hero.attack(self, enemy)  # 指名道姓,不依赖于继承,在子类中重用父类的方法accack
        print('from Garen Class')


class Riven(Hero):
    pass


g1 = Garen('ike', 100, 30)
l1 = Riven('joker', 90, 20)
print(l1.__dict__)
print(g1.__dict__)
print(l1.life_value)
g1.attack(l1)
print(l1.life_value)
View Code

方式二:

super()    依赖继承

class Hero:

    def __init__(self, nickname, life_value, aggressivity):
        self.nickname = nickname
        self.life_value = life_value
        self.aggressivity = aggressivity

    def attack(self, enemy):
        enemy.life_value -= self.aggressivity


class Garen(Hero):
    camp = 'Demacia'

    def __init__(self, nickname, life_value, aggressivity, weapon):
        # self.nickname = nickname
        # self.life_value = life_value
        # self.aggressivity = aggressivity
        # Hero.__init__(self, nickname, life_value, aggressivity)  # 调用父类的__init__参数要一致,不依赖于继承
        # super(Garen, self).__init__(nickname, life_value, aggressivity)  # python2 super(Garen, self)要加参数
        super().__init__(nickname, life_value, aggressivity)  # python3不用加参数,默认加参数

        self.weapon = weapon

    def attack(self, enemy):
        Hero.attack(self, enemy)  # 指名道姓,不依赖于继承,在子类中重用父类的方法accack
        print('from Garen Class')


g = Garen('草丛伦', 100, 30, '大刀')
print(g.__dict__)
View Code

这两种方式的区别是:方式一是跟继承没有关系的,而方式二的super()是依赖于继承的,并且即使没有直接继承关系,super仍然会按照mro继续往后查找

#A没有继承B,但是A内super会基于C.mro()继续往后找
class A:
    def test(self):
        super().test()
class B:
    def test(self):
        print('from B')
class C(A,B):
    pass

c=C()
c.test() #打印结果:from B


print(C.mro())
#[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
View Code

 组合

组合与重用性:

软件重用的重要方式除了继承之外还有另一种方式,--->组合

组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合.

组合与继承都是有效利用已有类的资源的重要方式,但是二者的概念和使用场景不同.

继承的方式:

1.通过继承建立了派生类与基类之前的关系,是一种是的关系,比如白马是马,人是动物.

2.当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好,比如老师是人,学生是人.

组合的方式:

用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如教授有生日,教授教python和linux课程,教授有学生s1、s2、s3...

class People:
    school = 'luffycity'

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


class Teacher(People):

    def __init__(self, name, age, sex, level, salary):
        super().__init__(name, age, sex)

        self.level = level
        self.salary = salary

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


class Student(People):
    school = 'luffycity'

    def __init__(self, name, age, sex, class_time):
        super().__init__(name, age, sex)

        self.class_time = class_time

    def learn(self):
        print('%s is learning' % self.name)


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

    def tell_info(self):
        print('课程名<%s> 课程价钱<%s> 课程周期<%s>' % (self.course_name, self.course_price, self.course_period))


class Date:
    def __init__(self, year, mon, day):
        self.year = year
        self.mon = mon
        self.day = day

    def tell_info(self):
        print('%s-%s-%s' % (self.year, self.mon, self.day))


stu1 = Student('zhagnaa', 28, 'male', '8:00')
python = Course('python', 3000, '3mons')  # 实例化一个python课程
d = Date(1988, 4, 20)

stu1.bith = d
stu1.bith.tell_info()  # 通过组合的方式,本身没有Date

stu1.course = python
print(stu1.course.__dict__)
stu1.course.tell_info()  # 通过组合的方式,本来没有课程
View Code

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

抽象类

抽象类的定义:

与java一样,python也有抽象类的概念但是同样需要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化.

为什么要有抽象类:

如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性.

从设计角度去看,如果类是从现实对象抽象而来的,那么抽象类就是基于类抽象而来的.

从实现角度来看,抽象类与普通类的不同之处在于:抽象类中只能有抽象方法(没有实现功能),该类不能被实例化,只能被继承,且子类必须实现抽象方法.

抽象类与接口:

  • 抽象类的本质还是类,指的是一组类的相似性,包括数据属性(如all_type)和函数属性(如read、write),而接口只强调函数属性的相似性.
  • 抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计.

在python中实现抽象类

import abc


class Animal(metaclass=abc.ABCMeta):  # 抽象类只能被继承,不能被实例化
    all_type = 'amimal'

    @abc.abstractmethod  # 继承后,加装饰器的,必须遵循类的方法
    def run(self):
        pass

    @abc.abstractmethod  # 继承后,加装饰器的,必须遵循类的方法
    def eat(self):
        pass


class People(Animal):
    def run(self):
        print('people is walking')

    def eat(self):
        print('people is eating')


class Pig(Animal):
    def run(self):
        print('pig is walking')

    def eat(self):
        print('pig is eating')


class Dog(Animal):
    def run(self):
        print('dog is walking')

    def eat(self):
        print('dog is eating')


peo = People()
pig = Pig()
dog = Dog()

peo.eat()
pig.eat()
dog.eat()

print(peo.all_type)
View Code

多态与多态性

多态:指的是同一类事物的多种形态.

多态性:指的是可以在不考虑对象的类型情况下而直接使用实例,多态性分为静态多态性和动态多态性.

静态多态性:任何类型都可以用运算符+进行运算

动态多态性:例子

import abc


# 多态:同一类事物的多种形态
class Animal(metaclass=abc.ABCMeta):  # 同一类事物:动物
    @abc.abstractmethod
    def talk(self):
        pass


class People(Animal):  # 动物的形态之一:人
    def talk(self):
        print('say hello')


class Dog(Animal):  # 动物的形态之二:狗
    def talk(self):
        print('say wangwang')


class Pig(Animal):  # 动物的形态之三:猪
    def talk(self):
        print('say aoao')


class Cat(Animal):
    def talk(self):
        print('say miaomiao')


# 多态性:指的是可以在不考虑对象的类型的情况下而直接使用对象
peo = People()
dog = Dog()
pig = Pig()
cat = Cat()

# peo.talk()
# dog.talk()
# pig.talk()
#

def func(mm):
    mm.talk()


func(peo)
func(cat)
func(pig)
func(dog)
View Code

多态性的好处:

增加了程序的灵活性:以不变应万变,不论对象千变万化,使用者都是同一种形式去调用,如:fun(animal)

增加了程序的可扩展性:通过继承animal类创建了一个新的类,使用者无需更改自己的代码,还是用func(animal)去调用

鸭子类型

Python崇尚鸭子类型,即‘如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子’.

python程序员通常根据这种行为来编写程序。例如,如果想编写现有对象的自定义版本,可以继承该对象,也可以创建一个外观和行为像,但与它无任何关系的全新对象,后者通常用于保存程序组件的松耦合度。

例1:利用标准库中定义的各种‘与文件类似’的对象,尽管这些对象的工作方式像文件,但他们没有继承内置文件对象的方法.

class File:
    def read(self):
        pass

    def write(self):
        pass


class Disk:
    def read(self):
        print('disk read')

    def write(self):
        print('disk write')


class Text:
    def read(self):
        print('text read')

    def write(self):
        print('text write')


disk = Disk()
text = Text()

disk.read()
disk.write()

text.read()
text.write()
View Code

例2:序列类型有多种形态:字符串,列表,元组,但他们直接没有直接的继承关系

序列类型:列表(list),元祖(tuple),字符串(str)
l = list([1, 2, 3])
t = tuple((1, 2, 3, 4, 5))
s = 'hahaha'


# print(l.__len__())
# print(t.__len__())
# print(s.__len__())


def len(w):
    return w.__len__()


print(len(l))
print(len(t))
print(len(s))
View Code

 封装

如何隐藏:

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

#其实这仅仅这是一种变形操作
#类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式:

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是可以访问到的,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形
View Code

这种自动变形的特点:

  • 类中定义的__x只能在内部使用,如self.__x,引用的就是变形的结果。
  • 这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的。
  • 在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。

这种变形需要注意的问题是:

  • 1、这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N
  • 2、变形的过程只在类的定义是发生一次,在定义后的赋值操作,不会变形
  •  

  • 3、在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的
# 正常情况
class Foo:
    def func(self):
        print('from foo')

    def bar(self):
        print('A.bar')
        self.func()


class Bar(Foo):
    def func(self):
        print('from bar')


b = Bar()
b.func()  # from bar


# 把__func设置成私有
class Foo:
    def __func(self):  # _Foo__func
        print('from foo')

    def bar(self):
        print('A.bar')
        self.__func()  # self._Foo__bar()


class Bar(Foo):
    def __func(self):
        print('from bar')


b = Bar()
b.bar()  # A.bar  from foo
View Code

封装的意义:

封装数据属性:明确的区分内外,控制外部对影藏的属性的操作行为
class People:
    def __init__(self, name, age):
        self.__name = name
        self.__age = age

    def tell_info(self):  # 内部开一个接口,外部调用
        print('Name:<%s> Age:<%s>' % (self.__name, self.__age))

    def set_info(self, name, age):  # 开一个接口外部调用
        if not isinstance(name, str):
            print('名字必须是字符串类型')
            return
        if not isinstance(age, int):
            print('年龄必须是数字类型')
            return
        self.__name = name
        self.__age = age


p = People('ike', 18)
p.tell_info()
p.set_info('ikeer', 8)
p.tell_info()
View Code
封装方法:隔离复杂度
#取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱
#对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来,很明显这么做
#隔离了复杂度,同时也提升了安全性


class ATM:

    def __card(self):
        print('插卡')

    def __auth(self):
        print('用户认证')

    def __input(self):
        print('输入取款金额')

    def __print_bill(self):
        print('打印账单')

    def __take_money(self):
        print('取款')

    def withdraw(self):
        self.__card()
        self.__auth()
        self.__input()
        self.__print_bill()
        self.__take_money()


a = ATM()
a.withdraw()
View Code

提示:在编程语言里,对外提供的接口(接口可理解为了一个入口),可以是函数,称为接口函数,这与接口的概念还不一样,接口代表一组接口函数的集合体。

特性(property)

property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值.

例一:BMI指数(bmi是计算而来的,但很明显它听起来像是一个属性而非方法,如果我们将其做成一个属性,更便于理解)

成人的BMI数值:

过轻:低于18.5

正常:18.5-23.9

过重:24-27

肥胖:28-32

非常肥胖, 高于32

体质指数(BMI)=体重(kg)÷身高^2(m)

EX:70kg÷(1.75×1.75)=22.86

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)


p = People('ike', 69, 1.73)
print(p.bmi)  # @property调用的时候不用加括号()
View Code

例二:圆的周长和面积

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
'''
View Code

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

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'
View Code

封装与扩展性

封装在于明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者的代码;而外部使用用者只知道一个接口(函数),只要接口(函数)名、参数不变,使用者的代码永远无需改变。这就提供一个良好的合作基础——或者说,只要接口这个基础约定不变,则代码改变不足为虑。

#类的设计者
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()
View Code

绑定方法与非绑定方法

绑定方法:

绑定给谁,谁来调用就自动将他本身当做第一个参数传入.

  • 绑定到类的方法-->用classmethod装饰的方法
  • 绑定到对象的方法-->没有被任何装饰器的方法

非绑定方法:

用staticmethod装饰器装饰的方法

class Foo:

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

    def tell(self):
        print('名字是%s' % self.name)

    @classmethod  # 绑定给类
    def func(cls):  # cls =Foo
        print(cls)

    @staticmethod  # 对象和类都可以调用
    def func1(x, y):
        print(x + y)


f = Foo('ike')
#
# f.tell()  # 调用tell方法
f.func()  # classmethod绑定给类,只能类来调用
print(Foo.func)

# 打印结果:
'''
名字是ike
<class '__main__.Foo'>
<bound method Foo.func of <class '__main__.Foo'>>
'''

# Foo.func1(1, 2)
# f.func1(5, 6)

# 打印结果:
'''
3
11
'''
View Code

classmethod与staticmethod对比

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

    def tell(self):  # 绑定到对象的方法
        print('Name:%s Age:%s Sex:%s' % (self.name, self.age, self.sex))

    @classmethod    # 绑定到类的方法
    def from_conf(cls):
        obj = cls(settings.name, settings.age, settings.sex)
        return obj

    @ staticmethod
    def create_id():
        m = hashlib.md5(str(time.time()).encode('utf-8'))
        return m.hexdigest()


p = People('ike', 18, 'male')

# 绑定给对象,就应该由对象来调用,自动将对象本身当做第一个参数传入
p.tell()


# 绑定给类,就应该由类来调用,自动将类本身当做第一个参数传入
p = People.from_conf()
p.tell()

# 非绑定方法,不与类或者对象绑定,谁都可以调用,没有自动传值一说
p1 = People('ike', 18, 'male')
p2 = People('ike', 18, 'male')
p3 = People('ike', 18, 'male')
print(id(p1))
print(id(p2))
print(id(p3))
View Code

反射

1.什么是反射:

反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。

2.python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)

四个可以实现自省的函数 下列方法适用于类和对象(一切皆对象,类本身也是一个对象)

hasattr(object,name)

  判断object中有没有name的属性或方法

getattr(object, name, default=None)     获取

def getattr(object, name, default=None): # known special case of getattr
    """
    getattr(object, name[, default]) -> value

    Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y.
    When a default argument is given, it is returned when the attribute doesn't
    exist; without it, an exception is raised in that case.
    """
    pass

setattr(x, y, v)    修改

def setattr(x, y, v): # real signature unknown; restored from __doc__
    """
    Sets the named attribute on the given object to the specified value.

    setattr(x, 'y', v) is equivalent to ``x.y = v''
    """
    pass

delattr(x, y)    删除

def delattr(x, y): # real signature unknown; restored from __doc__
    """
    Deletes the named attribute from the given object.

    delattr(x, 'y') is equivalent to ``del x.y''
    """
    pass

演示:

class People:
    country = 'China'

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

    def talk(self):
        print('%s is talking' % self.name)


obj = People('ike', 18)

# 判断有没有属性
print(hasattr(obj, 'name'))  # obj.name  # obj.__dict__['name']
print(hasattr(obj, 'talk'))  # obj.talk

# 获取
# print(getattr(obj, 'name', None))  # None没有属性不会报错,返回None
# print(getattr(obj, 'talk', None))

# 修改
# setattr(obj, 'sex', 'male')  # obj.sex = 'male'
# print(obj.__dict__)

# 删除
# delattr(obj, 'age')  # del obj.age
# print(obj.__dict__)
View Code

反射的应用:

class Service:
    def run(self):
        while True:
            inp = input('>>:').strip()
            cmd = inp.split()

            if hasattr(self, cmd[0]):  # 用户输入get  xx.txt
                func = getattr(self, cmd[0])
                func(cmd)

    def get(self, cmd):
        print('get....', cmd)

    def put(self, cmd):
        print('put.....', cmd)


obj = Service()
obj.run()
View Code

 

posted on 2019-04-07 20:24  cn_ike  阅读(300)  评论(0编辑  收藏  举报

导航