python基础6--面向对象
1、面向对象基本格式
class 类名:
def 方法名(self,name):
print(name)
return 123
#调用
# 1.创建该类的对象
obj = 类名()
# 2.通过对象调用方法
result = obj.方法名('jack')
print(result)
# 如果在python2中这样定义,则称其为:经典类
class Foo:
pass
# 如果在python2中这样定义,则称其为:新式类
class Foo(object):
pass
# 在python3中这俩的写法是一样,因为所有的类默认都会继承object类,全部都是新式类。
class Base(object):
pass
class Bar(Base):
pass
2、面向对象的特性
2.1封装
- 表现一:函数封装到类
class File:
def read(self):
pass
def write(self):
pass
- 表现二:数据封装到对象 *
class Person:
def __init__(sef,name,age):
self.name = name
self.age = age
p = Person('jack',19)
class Person:
def __init__(self,n,a,g): # 初始化方法(构造方法),给对象的内部做初始化。
self.name = n
self.age = a
self.gender = g
def show(self):
temp = "我是%s,年龄:%s,性别:%s " % (self.name, self.age, self.gender,)
print(temp)
# 类() 实例化对象,自动执行此类中的 __init__方法。
p1 = Person('jack',19,'男')
p1.show()
p2 = Person('jack',19,'男')
p2.show()
2.2继承
# 父类(基类)
class Base:
def f1(self):
pass
# 子类(派生类)
class Foo(Base):
def f2(self):
pass
# 创建了一个字类的对象
obj = Foo()
# 执行对象.方法时,优先在自己的类中找,如果没有就是父类中找。
obj.f2()
obj.f1()
什么时候才能用到继承?多个类中如果有公共的方法,可以放到基类中避免重复编写。
class Base:
def f1(self):
self.f2()
print('base.f1')
def f2(self):
print('base.f2')
class Foo(Base):
def f2(self):
print('foo.f2')
obj = Foo()
obj.f1()
#foo.f2
#base.f1 注意self是谁
注意事项:
- self 到底是谁?
- self 是哪个类创建的,就从此类开始找,自己没有就找父类。
多继承
Python的类如果继承了多个类,那么其寻找方法的方式有两种,分别是:深度优先和广度优先
- 当类是经典类时,多继承情况下,会按照深度优先方式查找
- 当类是新式类时,多继承情况下,会按照广度优先方式查找
属性或方法一旦找到,则寻找过程立即中断,便不会再继续找了
2.3多态
多态是指一类事物有多种形态,比如动物类,可以有猫,狗,猪等等。(一个抽象类有多个子类,因而多态的概念依赖于继承)
python原生多态,Python崇尚“鸭子类型”
# Python
def func(arg):
v = arg[-1] # arg.append(9)
print(v)
# java
def func(str arg):
v = arg[-1]
print(v)
什么是鸭子模型?
对于一个函数而言,Python对于参数的类型不会限制,那么传入参数时就可以是各种类型,在函数中如果有例如:arg.send方法,那么就是对于传入类型的一个限制(类型必须有send方法)。
这就是鸭子模型,类似于上述的函数我们认为只要能呱呱叫的就是鸭子(只有有send方法,就是我们要想的类型)
3、成员
变量:
- 实例变量
- 类变量
方法:
- 绑定方法
- 类方法
- 静态方法
属性:
- 普通属性
3.1实例变量
实例自己封装的变量,也叫普通字段
class Foo:
def __init__(self,name):
self.name=name
def info(self):
pass
obj1 = Foo("jack")
obj2 = Foo('yhp')
3.2类变量
-
定义:写在类的下一级和方法同一级。
-
访问:
类.类变量名称 (推荐) 对象.类变量名称
class Base: x = 1 obj = Base() print(obj.x) # 先去对象中找,没有再去类中找。 obj.y = 123 # 在对象中添加了一个y=123的变量。 print(obj.y) obj.x = 123 print(obj.x) print(Base.x)
总结:找变量优先找自己,自己没有找 类 或 基类;修改或赋值只能在自己的内部设置。
字段包括:普通字段和静态字段,他们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同,
- 实例变量属于对象
- 类变量属于类
3.3绑定方法(普通方法)
- 定义:至少有一个self参数
- 执行:先创建对象,由对象.方法()。
class Foo:
def func(self,a,b):
print(a,b)
obj = Foo()
obj.func(1,2)
# ###########################
class Foo:
def __init__(self):
self.name = 123
def func(self, a, b):
print(self.name, a, b)
obj = Foo()
obj.func(1, 2)
3.4静态方法
- 定义:
- @staticmethod装饰器
- 参数无限制
- 执行:
- 类.静态方法名 ()
- 对象.静态方法() (不推荐)
class Foo:
def __init__(self):
self.name = 123
def func(self, a, b):
print(self.name, a, b)
@staticmethod
def f1():
print(123)
obj = Foo()
obj.func(1, 2)
Foo.f1()
obj.f1() # 不推荐
3.5类方法
- 定义:
- @classmethod装饰器
- 至少有cls参数,当前类。
- 执行:
- 类.类方法()
- 对象.类方法() (不推荐)
class Foo:
def __init__(self):
self.name = 123
def func(self, a, b):
print(self.name, a, b)
@staticmethod
def f1():
print(123)
@classmethod
def f2(cls,a,b):
print('cls是当前类',cls)
print(a,b)
obj = Foo()
obj.func(1, 2)
Foo.f1()
Foo.f2(1,2)
总结:
方法包括:普通方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同。
- 普通方法:由对象调用;至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self;
- 类方法:由类调用; 至少一个cls参数;执行类方法时,自动将调用该方法的类复制给cls;
- 静态方法:由类调用;无默认参数;
@classmethod和@staticmethod的区别?
一个是类方法一个静态方法。
定义:
类方法:用@classmethod做装饰器且至少有一个cls参数。
静态方法:用staticmethod做装饰器且参数无限制。
调用:
类.方法直接调用。不用实例化可直接调用
对象.方法也可以调用。
3.6属性
- 定义:
- @property装饰器
- 只有一个self参数
- 执行:
- 对象.方法 不用加括号。
class Foo:
@property
def func(self):
print(123)
return 666
obj = Foo()
result = obj.func
print(result)
# 属性的应用
class Page:
def __init__(self, total_count, current_page, per_page_count=10):
self.total_count = total_count
self.per_page_count = per_page_count
self.current_page = current_page
@property
def start_index(self):
return (self.current_page - 1) * self.per_page_count
@property
def end_index(self):
return self.current_page * self.per_page_count
USER_LIST = []
for i in range(321):
USER_LIST.append('jack-%s' % (i,))
# 请实现分页展示:
current_page = int(input('请输入要查看的页码:'))
p = Page(321, current_page)
data_list = USER_LIST[p.start_index:p.end_index]
for item in data_list:
print(item)
总结:类方法,是直接对类操作时候定义的,实例化方法是操作实例化对象时候定义的,静态方法与类无关,就是独立的逻辑单元。@property是将方法转为属性,直接可使用其返回值,提供了可读可写可删除的操作,如果像只读效果,就只需要定义@property就可以,不定义代表禁止其他操作。
4、 成员修饰符
- 公有,所有地方都能访问到。
- 私有,只有自己可以访问到。
--
开头的变量
class Foo:
def __init__(self, name):
self.__name = name
def func(self):
print(self.__name)
obj = Foo('alex')
# print(obj.__name)
obj.func()
class Foo:
__x = 1
@staticmethod
def func():
print(Foo.__x)
# print(Foo.__x)
Foo.func()
class Foo:
def __fun(self):
print('msg')
def show(self):
self.__fun()
obj = Foo()
# obj.__fun()
obj.show()
# 强制访问私有成员
# 非要访问私有属性的话,可以通过 对象._类__属性名
class Foo:
def __init__(self,name):
self.__x = name
obj = Foo('alex')
print(obj._Foo__x) # 强制访问私有实例变量
5 、特殊成员
5.1 __init__
初始化方法,实例化类时,自动触发执行,为对象赋初值
class Foo:
def __init__(self,a1):
"""
初始化方法
"""
self.a1 = a1
obj = Foo('Jack')
5.2__new__
用于创建空对象,构造方法
class Foo(object):
def __new__(cls, *args, **kwargs):
"""
用于创建空对象,构造方法
:param args:
:param kwargs:
:return:
"""
return object.__new__(cls)
obj = Foo()
5.3 __call__
对象加()执行该方法
class Foo(object):
def __call__(self, *args, **kwargs):
print('执行call方法')
# obj = Foo()
# obj()
Foo()()
from wsgiref.simple_server import make_server
def func(environ,start_response):
start_response("200 OK", [('Content-Type', 'text/plain; charset=utf-8')])
return ['你好'.encode("utf-8")]
class Foo(object):
def __call__(self, environ,start_response):
start_response("200 OK", [('Content-Type', 'text/html; charset=utf-8')])
return ['你<h1 style="color:red;">不好</h1>'.encode("utf-8")]
# 作用:写一个网站,用户只要来方法,就自动找到第三个参数并执行。
# server = make_server('127.0.0.1', 8000, func)
server = make_server('127.0.0.1', 8000, Foo())
server.serve_forever()
5.4 __getitem__
class Foo(object):
def __setitem__(self, key, value):
pass
def __getitem__(self, item):
return item + 'uuu'
def __delitem__(self, key):
pass
obj1 = Foo()
obj1['k1'] = 123 # 内部会自动调用 __setitem__方法
val = obj1['xxx'] # 内部会自动调用 __getitem__方法
print(val)
del obj1['ttt'] # 内部会自动调用 __delitem__ 方法
5.5 __str__
如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值。
class User(object):
def __init__(self,name,email):
self.name = name
self.email = email
def __str__(self):
return "%s %s" %(self.name,self.email,)
jack=User('jack','jack@qq.com')
print(jack)
5.6__dict__
class Foo(object):
def __init__(self,name,age,email):
self.name = name
self.age = age
self.email = email
obj = Foo('jack',19,'jack@qq.com')
val = obj.__dict__ # 去对象中找到所有变量并将其转换为字典
print(val)
# {'name': 'jack', 'age': 19, 'email': 'jack@qq.com'}
5.7 __enter__
实例化对象的时候,并不会调用__enter__方法,只有进入with语句体中,才会调用__enter__方法,然后执行语句体,最后离开with语句块的时候,再调用__exit__方法.
要想自己写的类实现上下文管理,那么需要用到两个方法__exit__和__enter__.
方法 | 意义 |
---|---|
enter | 进入与此对象相关的上下文,如果存放该方法,with语法会把该方法的返回值绑定到as子句中指定的变量上 |
exit | 退出与此对象相关的上下文 |
class Foo(object):
def __enter__(self):
self.x = open('a.txt',mode='a',encoding='utf-8')
return self.x
def __exit__(self, exc_type, exc_val, exc_tb):
self.x.close()
with Foo() as ff:
ff.write('abc')
5.8 __add__
class Foo(object):
def __add__(self, other):
return 123
obj1 = Foo()
obj2 = Foo()
val = obj1 + obj2
print(val)
6、面向对象相关内置函数
6.1 type
class Foo:
pass
obj = Foo()
if type(obj) == Foo:
print('obj是Foo类的对象')
6.2 issubclass
issubclass(sub,super):检查sub是不是super的子类(传两个参数,一个是子类,一个是父类)
class Base:
pass
class Base1(Base):
pass
class Foo(Base1):
pass
class Bar:
pass
print(issubclass(Bar,Base))
print(issubclass(Foo,Base))
6.3 isinstance
isinstance(obj,cls):检查obj是不是cls的对象(传两个参数,一个是对象,一个是类),包括继承
class Base(object):
pass
class Foo(Base):
pass
obj = Foo()
print(isinstance(obj,Foo)) # 判断obj是否是Foo类或其基类的实例(对象)
print(isinstance(obj,Base)) # 判断obj是否是Foo类或其基类的实例(对象)判断类实例最好用type
6.4 super
class Base(object):
def func(self):
print('base.func')
return 123
class Foo(Base):
def func(self):
v1 = super().func()
print('foo.func',v1)
obj = Foo()
obj.func()
# super().func() 按mro继承顺序下一个去找
class Bar(object):
def func(self):
print('bar.func')
return 123
class Base(Bar):
pass
class Foo(Base):
def func(self):
v1 = super().func()
print('foo.func',v1)
obj = Foo()
obj.func()
# super().func() 根据类的继承关系,按照顺序挨个找func方法并执行(找到第一个就不在找了)
class Base(object): # Base -> object
def func(self):
super().func()
print('base.func')
class Bar(object):
def func(self):
print('bar.func')
class Foo(Base,Bar): # Foo -> Base -> Bar
pass
obj = Foo()
obj.func()
# super().func() 根据self对象所属类的继承关系,按照顺序挨个找func方法并执行(找到第一个就不在找了)
7、接口类
约束子类必许实现某些方法,否则抛出异常。
# 约束字类中必须写send方法,如果不写,则调用时候就报抛出 NotImplementedError
class Interface(object):
def send(self):
raise NotImplementedError()
class Message(Interface):
def send(self):
print('发送短信')
class Email(Interface):
def send(self):
print('发送邮件')
8、反射
反射是通过字符串的形式操作对象相关的成员。一切事物都是对象!!!
python中的反射功能是由以下四个内置函数提供:hasattr、getattr、setattr、delattr,该四个函数分别用于对对象内部执行:检查是否含有某成员、获取成员、设置成员、删除成员。
-
getattr(对象,"字符串") 根据字符串的形式去某个对象中 获取 对象的成员.
class Foo(object): def __init__(self,name): self.name = name obj = Foo('yhp') # 获取变量 v1 = getattr(obj,'name') # 获取方法 method_name = getattr(obj,'login') method_name()
-
hasattr(对象,'字符串') 根据字符串的形式去某个对象中判断是否有该成员。
#!/usr/bin/env python # -*- coding:utf-8 -*- from wsgiref.simple_server import make_server class View(object): def login(self): return '登陆' def logout(self): return '等出' def index(self): return '首页' def func(environ,start_response): start_response("200 OK", [('Content-Type', 'text/plain; charset=utf-8')]) # obj = View() # 获取用户输入的URL method_name = environ.get('PATH_INFO').strip('/') if not hasattr(obj,method_name): return ["sdf".encode("utf-8"),] response = getattr(obj,method_name)() return [response.encode("utf-8") ] # 作用:写一个网站,用户只要来方法,就自动找到第三个参数并执行。 server = make_server('192.168.12.87', 8000, func) server.serve_forever()
-
setattr(对象,'变量','值') 根据字符串的形式去某个对象中设置成员。
class Foo: pass obj = Foo() obj.k1 = 999 setattr(obj,'k1',123) # obj.k1 = 123 print(obj.k1)
-
delattr(对象,'变量') 根据字符串的形式去某个对象中删除成员。
class Foo: pass obj = Foo() obj.k1 = 999 delattr(obj,'k1') print(obj.k1)
python一切皆对象
- py文件
- 包
- 类
- 对象
python一切皆对象,所以以后想要通过字符串的形式操作其内部成员都可以通过反射的机制实现。
import sys
# 反射文件中任意内容
def s2():
print('s2')
this_module = sys.modules[__name__]
if hasattr(this_module, 's2'):
getattr(this_module, 's2')()
9、单例模式
无论实例化多少次,永远用的都是第一次实例化出的对象。
单利模式存在的目的是保证当前内存中仅存在单个实例,避免内存浪费!!!
应用场景:线程池,数据库连接池等资源池,logger
class Foo:
pass
# 多例,每实例化一次就创建一个新的对象。
obj1 = Foo() # 实例,对象
obj2 = Foo() # 实例,对象
# 单例,无论实例化多少次,都用第一次创建的那个对象。
obj1 = Foo()
obj2 = Foo()
单例模式标准
class Singleton(object):
instance = None
def __new__(cls, *args, **kwargs):
if not cls.instance:
cls.instance = object.__new__(cls)
return cls.instance
obj1 = Singleton()
obj2 = Singleton()
# 不是最终,加锁。并发中会不安全,抢占该实例
带锁的单例
from threading import Lock,Thread
class Singleton(object):
__instance = None
lock = Lock()
def __new__(cls, *args, **kwargs):
with cls.lock:
if not cls.__instance:
cls.__instance = super().__new__(cls)
return cls.__instance
文件的连接池
class FileHelper(object):
instance = None
def __init__(self, path):
self.file_object = open(path,mode='r',encoding='utf-8')
def __new__(cls, *args, **kwargs):
if not cls.instance:
cls.instance = object.__new__(cls)
return cls.instance
obj1 = FileHelper('x')
obj2 = FileHelper('x')
- 模块多次导入重新加载
import jd # 第一次加载:会加载一遍jd中所有的内容。
import jd # 由已经加载过,就不在加载。
print(456)
重载
import importlib
import jd
importlib.reload(jd)
print(456)
通过模块导入的特性也可以实现单例模式:
# jd.py
class Foo(object):
pass
obj = Foo()
# app.py
import jd # 加载jd.py,加载最后会实例化一个Foo对象并赋值给obj
print(jd.obj)
10、 异常处理
10.1基本格式
try:
pass
except Exception as e:
logHanler.write_log(e)
Exception可以捕获任意异常,其他异常可以对异常进行分类
try:
v = []
v[11111] # IndexError
except ValueError as e:
pass
except IndexError as e:
pass
except Exception as e:
print(e) # e是Exception类的对象
finally关键字
try:
int('abc')
except Exception as e:
print(e)
finally:
print('最后无论对错都会执行')
# 特殊情况
def func():
try:
int('abc')
except Exception as e:
print(e) #
return 123 # 即使有return也会执行finally
finally:
print('最后')
func()
10.2 主动触发异常
try:
int('123')
raise Exception('转换失败') # 代码中主动抛出异常
except Exception as e:
print(e)
10.3 自定义异常
class MyException(Exception):
def __init__(self, msg):
super().__init__()
self.message = msg
def __str__(self):
return self.message
try:
raise MyException('我的异常')
except MyException as e:
print(e)
其他常见异常
AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x
IOError 输入/输出异常;基本上是无法打开文件
ImportError 无法引入模块或包;基本上是路径问题或名称错误
IndentationError 语法错误(的子类) ;代码没有正确对齐
IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]
KeyError 试图访问字典里不存在的键
KeyboardInterrupt Ctrl+C被按下
NameError 使用一个还未被赋予对象的变量
SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了)
TypeError 传入对象类型与要求的不符合
UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,
导致你以为正在访问它
ValueError 传入一个调用者不期望的值,即使值的类型是正确的
补充点
11、元类
什么是元类呢?在Python3中继承type的就是元类
class ASD(type):
pass
qqq = ASD("qwe", (object,), {}) #用ASD这个元类创建了一个(qwe,并且继承object类的)类
# class ASD(qwe):
# pass
obj = qqq()
# 能创建类的是元类
# 能创建对象的是类
print(obj) #<__main__.qwe object at 0x00000000024FFBA8>
print(obj.__class__) #<class '__main__.qwe'>
print(obj.__class__.__class__) #<class '__main__.ASD'>
print(obj.__class__.__class__.__class__) #<class 'type'>
print(obj.__class__.__class__.__class__.__class__) #<class 'type'>