目录
一. 类与对象
二. 继承
三. 多态与多态性
四. 封装
五. 绑定方法与非绑定方法
六. 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'
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'))

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)

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'))
# 组合应用 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)
反射示范:
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()