目录
一. 类与对象
二. 继承
三. 多态与多态性
四. 封装
五. 绑定方法与非绑定方法
六. 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:\Users\Administrator\PycharmProjects\test\面向对象编程\test1\db'
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()
浙公网安备 33010602011771号