PYTHON_DAY_07

目录

一. 类与对象

二. 继承

三. 多态与多态性

四. 封装

五. 绑定方法与非绑定方法

六. staticmethod 与 classmethod 的区别

七. 内置方法

八. 反射


一. 类与对象

 类(Class)定义了一件事物的抽象特点。通常来说,类定义了事物的属性和它可以做到的(它的行为)。举例来说,“狗”这个类会包含狗的一切基础特征,即所有“狗”都共有的特征或行为,例如它的孕育、毛皮颜色和吠叫的能力。类可以为程序提供模版和结构。一个类的方法和属性被称为“成员”。 在代码中,我们声明了一个类,这个类具有一些狗的基本特征。

  

类 狗
开始
公有成员:
    吠叫():
私有成员:
    毛皮颜色:
    孕育:结束

  

对象

  对象(Object)是类的实例。例如,“狗”这个类列举狗的特点,从而使这个类定义了世界上所有的狗。而莱丝这个对象则是一条具体的狗,它的属性也是具体的。狗有皮毛颜色,而莱丝的皮毛颜色是棕白色的。因此,莱丝就是狗这个类的一个实例。一个具体对象属性的值被称作它的“状态”。(系统给对象分配内存空间,而不会给类分配内存空间。这很好理解,类是抽象的系统不可能给抽象的东西分配空间,而对象则是具体的。)

定义莱丝是狗
莱丝.毛皮颜色:棕白色
莱丝.吠叫()

  

  我们无法让狗这个类去吠叫,但是我们可以让对象“莱丝”去吠叫,正如狗可以吠叫,但没有具体的狗就无法吠叫。类和对象就好比是“实型”和“1.23”,“实型”是一种数据的类型,而“1.23”是一个真正的“实数”(即对象)。所有的“实数”都具有“实型”所描述的特征,如“实数的大小”,系统则分配内存给“实数”存储具体的数值。

消息传递

  一个对象通过接受消息、处理消息、传出消息或使用其他类的方法来实现一定功能,这叫做消息传递机制(Message Passing)。

  如:莱丝可以通过吠叫引起人的注意,从而导致一系列的事发生。

 

二. 继承

继承

  继承性(Inheritance)是指,在某种情况下,一个类会有“子类”。子类比原本的类(称为父类)要更加具体化。例如,“狗”这个类可能会有它的子类“牧羊犬”和“吉娃娃犬”。在这种情况下,“莱丝”可能就是牧羊犬的一个实例。子类会继承父类的属性和行为,并且也可包含它们自己的。我们假设“狗”这个类有一个方法(行为)叫做“吠叫()”和一个属性叫做“毛皮颜色”。它的子类(前例中的牧羊犬和吉娃娃犬)会继承这些成员。这意味着程序员只需要将相同的代码写一次。

在伪代码中我们可以这样写:

类牧羊犬:继承狗
 
定义莱丝是牧羊犬
莱丝.吠叫()            /* 注意这里调用的是狗这个类的吠叫方法。    

  

  回到前面的例子,“牧羊犬”这个类可以继承“毛皮颜色”这个属性,并指定其为棕白色。而“吉娃娃犬”则可以继承“吠叫()”这个方法,并指定它的音调高于平常。子类也可以加入新的成员,例如,“吉娃娃犬”这个类可以加入一个方法叫做“颤抖()”。设若用“牧羊犬”这个类定义了一个实例“莱丝”,那么莱丝就不会颤抖,因为这个方法是属于吉娃娃犬的,而非牧羊犬。事实上,我们可以把继承理解为“是”或“属于”。莱丝“是”牧羊犬,牧羊犬“属于”狗类。因此,莱丝既得到了牧羊犬的属性,又继承了狗的属性。 我们来看伪代码:
类吉娃娃犬:继承狗
开始
公有成员:
    颤抖()
结束
 
类牧羊犬:继承狗
定义莱丝是牧羊犬
莱丝.颤抖()    /* 错误:颤抖是吉娃娃犬的成员方法。 */

  

当一个类从多个父类继承时,我们称之为“多重继承”。如一只狗既是吉娃娃犬又是牧羊犬(虽然事实上并不合逻辑)。多重继承并不总是被支持的,因为它很难理解,又很难被好好使用。
 

三. 多态与多态性

  A. 多态

  多态是指由继承而产生的相关的,不同的类,其对象对同一消息做出不同的响应。

  例如1:

    狗和鸡都有“叫()”这一方法,但是调用狗的“叫()”,狗会吠叫;

    调用鸡的“叫()”,鸡则会啼叫。

    虽然同样是做出叫这一行为,但狗和鸡具体做出的表现方式将大不相同。

  例如2:

    1. 序列类型有多种形态:字符串,列表,元组。

    2. 动物有多种形态:人,狗,猪

    3. 文件有多种形态:文本文件,可执行文件

#!/usr/bin/python
# -*- coding:utf-8 -*-

#多态是同一种事物的多种形态
class Animal:
    def talk(self):
        print('正在叫')


class People(Animal):
    def talk(self):
        print('say hello')

class Pig(Animal):
    def talk(self):
        print('哼哼哼')

class Dog(Animal):
    def talk(self):
        print('汪汪汪')


class Cat(Animal):
    def talk(self):
        print('喵喵喵')

peo1=People()
pig1=Pig()
dog1=Dog()
cat1=Cat()


peo1.talk()
dog1.talk()
pig1.talk()

  B. 多态性

  多态性是指具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同功能的函数。

  多态性:向不同的对象发送同一条消息(!!!obj.func():是调用了obj的方法func,又称为向obj发送了一条消息func),不同的对象在接收时会产生不同的行为(即方法)。

  也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。

  比如:老师.下课铃响了(),学生.下课铃响了(),老师执行的是下班操作,学生执行的是放学操作,虽然二者消息一样,但是执行的效果不同。

#多态性
def func(obj):  # obj-可以任意命名
    obj.talk()

func(peo1)
func(pig1)
func(dog1)
func(cat1)

  

1 say hello
2 哼哼哼
3 汪汪汪
4 喵喵喵
运行结果

   多态性的好处:

  1.增加了程序的灵活性

  以不变应万变,不论对象千变万化,使用者都是同一种形式去调用,如func(animal)

  2.增加了程序的可扩展性

  通过继承animal类创建了一个新的类,使用者无需更改自己的代码,还是用func(animal)去调用  

>>> class Cat(Animal): #属于动物的另外一种形态:猫
...     def talk(self):
...         print('say miao')
... 
>>> def func(animal): #对于使用者来说,自己的代码根本无需改动
...     animal.talk()
... 
>>> cat1=Cat() #实例出一只猫
>>> func(cat1) #甚至连调用方式也无需改变,就能调用猫的talk功能
say miao

'''
这样我们新增了一个形态Cat,由Cat类产生的实例cat1,使用者可以在完全不需要修改自己代码的情况下。使用和人、狗、猪一样的方式调用cat1的talk方法,即func(cat1)
'''

  

四. 封装

封装性

  具备封装性(Encapsulation)的面向对象程序设计隐藏了某一方法的具体执行步骤,取而代之的是通过消息传递机制传送消息给它。因此,举例来说,“狗”这个类有“吠叫()”的方法,这一方法定义了狗具体该通过什么方法吠叫。但是,莱丝的朋友并不知道它到底是如何吠叫的。

  封装是通过限制只有特定类的对象可以访问这一特定类的成员,而它们通常利用接口实现消息的传入传出。举个例子,接口能确保幼犬这一特征只能被赋予狗这一类。通常来说,成员会依它们的访问权限被分为3种:公有成员、私有成员以及保护成员

封装分为俩个层面

  第一个层面的封装(什么都不用做):创建类和对象会分别创建二者的名称空间,我们只能用类名.或者obj.的方式去访问里面的名字,这本身就是一种封装

  注意:对于这一层面的封装就是访问隐藏属性的接口(类名. 和 实例名.

class Foo:
    x=1
    def test(self):
        print('from test')

print(Foo.x)                  # 直接访问名字

  第二个层面的封装:类中把某些属性和方法隐藏起来(或者说定义成私有的),只在类的内部使用、外部无法访问,或者留下少量接口(函数)供外部访问。

语法结构的解释:

class Foo:
    __x=1         # 相当于 _Foo__x
    def __test(self):          # 相当于 _Foo__test
        print('from test')

print(Foo.__dict__)
Foo.test()                         # 报错调用失败 这个接口不可用了
print(Foo._Foo__x)
Foo._Foo__test(123)    

  只在类的内部使用、外部无法访问,示例如下:

class People:
    __country='China'
    def __init__(self,name,age,sex):
        self.__name=name #self._People__name=name
        self.__age=age
        self.__sex=sex

    def tell_info(self):
        print('人的名字是:%s ,人的性别是:%s,人的年龄是:%s' %(
            self.__name, #p._People__name
            self.__age,
            self.__sex))

p=People('alex',18,'male')
print(p.__dict__)   # {'_People__name': 'alex', '_People__age': 18, '_People__sex': 'male'}
p.tell_info()
# print(p.__name)  #  AttributeError: 'People' object has no attribute 'People__name' 对象木有这个属性 外界访问不到的
print(p._People__name)  # 正确


p.__salary=3000 #{'_People__name': 'alex', '_People__age': 18, '_People__sex': 'male', '__salary': 3000}
                # 可以看出只有初始化的被改变了
print(p.__dict__)

print(People.__dict__)

People.__n=11111111111111111111111111
print(People.__dict__)

  

这种自动变形的特点:

1.类中定义的__x只能在内部使用,如self.__x,引用的就是变形的结果

2.这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的。

3.在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。

注意:对于这一层面的封装(隐藏),我们需要在类中定义一个函数(接口函数)在它内部访问被隐藏的属性,然后外部就可以使用了

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

  

#正常情况
>>> class A:
...     def fa(self):
...         print('from A')
...     def test(self):
...         self.fa()
... 
>>> class B(A):
...     def fa(self):
...         print('from B')
... 
>>> b=B()
>>> b.test()
from B

  

#把fa定义成私有的,即__fa
>>> class A:
...     def __fa(self): #在定义时就变形为_A__fa
...         print('from A')
...     def test(self):
...         self.__fa() #只会与自己所在的类为准,即调用_A__fa
... 
>>> class B(A):
...     def __fa(self):
...         print('from B')
... 
>>> b=B()
>>> b.test()
from A

  

制作一个接口:

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

    def tell_info(self):
        print('人的名字是:%s ,人的年龄是:%s' %(
                    self.__name, # p._People__name
                    self.__age))

    def set_info(self,x,y):                       # 提供一个接口,并且使用者可以更改内容
        if not isinstance(x,str):                      # 防止胡乱使用时,接口提供了一个判断类型机制
            raise TypeError('名字必须是字符串类型')
        if not isinstance(y,int):
            raise TypeError('年龄必须是整数类型')

        self.__name=x
        self.__age=y

p=People('alex',1000)
p.tell_info()

p.set_info('alex_SB',123)     # 更改内容
p.tell_info()

  

property(内置函数)

1.基本语法

# property 基本语法

class Foo:
    @property
    def test(self):
        print('from fooo')
    # test=property(test)         # property相当于把test本身返回

f=Foo()
# f.test()
f.test        # 有了装饰器property test是数据属性 不用加括号直接用

  

2.应用

# 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)

p=People('egon',75,1.80)
p.height=1.82
# print(p.bmi())
print(p.bmi)            # 使用了装饰器后的属性方法不用括号直接用

  

3.应用(对封装添加接口时,用装饰器来修饰添加想要的逻辑(如更改、删除...)来控制外界的访问)

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

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

    @name.setter                    # 做了一个设置需求
    def name(self,value):
        if not isinstance(value,str):                   # 类型判断
            raise TypeError('名字必须是字符串类型')
        self.__name=value

    @name.deleter                   # 做了一个删除需求
    def name(self):
        if not self.permmission:                        # 默认权限关闭 不允许删除操作
            raise PermissionError('不允许的操作')
        del self.__name                                 # 这里是真是的删除语句__name
                                                        # 下面的del p.name是调用装饰器的封装函数

p=People('egon')

print(p.name)
#
p.name='egon666'
print(p.name)           # 在没做设置装饰器前 用户是不能赋值的

# p.name=35357
p.permmission=True      # 开启权限
del p.name

  

五. 绑定方法与非绑定方法  

类中定义的函数分成两大类:

  一:绑定方法(绑定给谁,谁来调用就自动将它本身当作第一个参数传入):

    1. 绑定到类的方法:用classmethod装饰器装饰的方法。

    为类量身定制

    类.boud_method(),自动将类当作第一个参数传入

    (其实对象也可调用,但仍将类当作第一个参数传入)

    2. 绑定到对象的方法:没有被任何装饰器装饰的方法。

    为对象量身定制

    对象.boud_method(),自动将对象当作第一个参数传入

    (属于类的函数,类可以调用,但是必须按照函数的规则来,没有自动传值那么一说)

  二:非绑定方法:用staticmethod装饰器装饰的方法

   1. 不与类或对象绑定,类和对象都可以调用,但是没有自动传值那么一说。就是一个普通工具而已

  注意:与绑定到对象方法区分开,在类中直接定义的函数,没有被任何装饰器装饰的,都是绑定到对象的方法,可不是普通函数,对象调用该方法会自动传值,而staticmethod装饰的方法,不管谁来调用,都没有自动传值一说

示例 1. 绑定方法

1 HOST='127.0.0.1'
2 PORT=3306
3 DB_PATH=r'C:UsersAdministratorPycharmProjects	est面向对象编程	est1db'
setting.py
import settings
class MySQL:
    def __init__(self,host,port):
        self.host=host
        self.port=port
        print('conneting...')

    # @classmethod                # 绑定到类的方法
    # def from_conf(cls):
    #     return cls(settings.HOST,settings.PORT)  # MySQL('127.0.0.1',3306)  调用配置文件里HOST、PORT

    @staticmethod               # 非绑定的方法 不与类或者对象绑定 谁都可以调用
    def from_conf():
        return MySQL(settings.HOST,settings.PORT)

    def select(self):           # 绑定到对象的方法
        print(self)
        print('select function')

# # conn=MySQL('192.168.1.3',3306)
# # conn.select()
#
# conn1=MySQL('192.168.1.3',3306)
conn2=MySQL.from_conf()             # 使用类方法来实例化
print(conn2.host,conn2.port)

  

 示例 2. 非绑定方法  添加 @staticmethod 

import hashlib
import time
import settings
class MySQL:
    def __init__(self,host,port):
        self.id=self.create_id()  # 非绑定类型 直接调用 不需要self返回值
        self.host=host
        self.port=port
        print('conneting...')

    @staticmethod
    def create_id(): #非绑定方法,就是类中的普通工具包
        m=hashlib.md5(str(time.clock()).encode('utf-8'))  # 哈希随机生成一个机器时间id
        return m.hexdigest()

    @classmethod
    def from_conf(cls):
        return cls(settings.HOST,settings.PORT) #MySQL('127.0.0.1',3306)

    def select(self): #绑定到对象的方法
        print(self)
        print('select function')

conn1=MySQL.from_conf()
conn2=MySQL.from_conf()
conn3=MySQL.from_conf()
conn4=MySQL.from_conf()

print(conn1.id)
print(conn2.id)
print(conn3.id)
print(conn4.id)

  

六. staticmethod 与 classmethod 的区别

 1 #statcimethod 与classmethod的区别
 2 import settings
 3 class MySQL:
 4     def __init__(self,host,port):
 5         self.host=host
 6         self.port=port
 7 
 8     @classmethod
 9     def from_conf(cls):
10         return cls(settings.HOST,settings.PORT) #Mariadb('127.0.0.1',3306)
11 
12     # @staticmethod
13     # def from_conf():
14     #     return MySQL(settings.HOST, settings.PORT)  # 这样的话实例化对象就写死在MySQL类中,下面的Mariab子类继承就不能实例化了
15 
16     def __str__(self):
17         return '就不告诉你'
18 # conn=MySQL.from_conf()
19 # print(conn.host)
20 
21 class Mariab(MySQL):
22     def __str__(self):
23         return '这是子类'
24 
25 conn1=Mariab.from_conf()    # 此时from_conf 不能调自己的方法 因为被绑定了<__main__.MySQL object at 0x0000013FE0D0E6D8>
26                             # conn1就是父类产生的 所以@staticmethod 不适用 我们来开启@classmethod 方法来解决
27 print(conn1)
示例代码

七. 内置方法

isinstance(obj,cls)检查是否obj是否是类 cls 的对象

class Foo(object):
    pass
 
 obj = Foo()
  
 print(isinstance(obj, Foo))

  

issubclass(sub, super)检查sub类是否是 super 类的派生类

class Foo:
    pass

class Bar(Foo):
    pass

print(issubclass(Bar,Foo))

  

八. 反射

  定义:程序可以访问、检测和修改它本身状态或行为的一种能力(自省)

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

  四个可以实现自省的函数:

  下列方法适用于类和对象(一切皆对象,类本身也是一个对象)

 1 # 判断object中有没有一个name字符串对应的方法或属性
 2 class Chinese:
 3     country='China'
 4     def __init__(self,name,age):
 5         self.name=name
 6         self.age=age
 7 
 8 p=Chinese('egon',18)
 9 
10 print(hasattr(p,'name'))
11 print(hasattr(Chinese,'country'))
hasattr(object,name)
 1 # setattr 设置进去值
 2 
 3 class Chinese:
 4     country='China'
 5     def __init__(self,name,age):
 6         self.name=name
 7         self.age=age
 8 
 9 p=Chinese('egon',18)
10 
11 p.x=1
12 print(p.__dict__)
13 print(p.x)
14 setattr(p,'x',1231231231231)
15 print(p.__dict__)
16 print(p.x)
setattr(x,y,v)
 1 # getattr 找值 找不到就第三个参数提示
 2 
 3 class Chinese:
 4     country='China'
 5     def __init__(self,name,age):
 6         self.name=name
 7         self.age=age
 8 
 9 p=Chinese('egon',18)
10 
11 print(getattr(p,'x','not exist'))
12 print(getattr(p,'name','not exist'))
getattr(object, name, default=None)
# 组合应用

setattr(p,'x',123123123123123) #添加
if hasattr(p,'x'):             #判断
    res=getattr(p,'x')         #获取
    print(res)

  

# 反射当前模块 组合应用

import sys
m=sys.module[__name__]

if hasattr(m,'chinese'):
    res = getattr(m,'chinese')
    print(res)
    
    obj=res('egon',18)             # 也可以实例化 反射name
    print(obj.name)    

  

 1 # 删除
 2 
 3 class Chinese:
 4     country='China'
 5     def __init__(self,name,age):
 6         self.name=name
 7         self.age=age
 8 
 9 p=Chinese('egon',18).
10 
11 print(Chinese.country)
12 delattr(Chinese,'country')
13 print(Chinese.country)
delattr(x,y)

反射示范:

class FtpCLient:
    def __init__(self,host):
        self.host=host
        print('connecting...')

    def run(self):
        while True:
            inp=input('>>: ').strip()
            inp_l=inp.split()
            if hasattr(self,inp_l[0]):
                func=getattr(self,inp_l[0])
                func(inp_l)

    def get(self,arg):
        print('download file',arg[1])

f=FtpCLient('192.168.1.2')
f.run()

  

补充:

父类与子类都有相同方法test时,子类调用父类的方式:super().test()

原文地址:https://www.cnblogs.com/lipingzong/p/6991840.html