Python记3(类与对象、路径、文件、异常处理、抽象基类

1、Class

  • 对象是类的实例

1.1、声明类:

class 类名: 类
  属性=初值
  方法(self,参数2,参数3…)

1.2、类的特殊属性:

name 类的名字(字符串)
doc 类的文档字符串
bases 类的所有父类组成的元组
dict 类的属性组成的字典
module 类所属模块
class 类对象的类型

1.3、创建对象:

class Person():     #创建类
    money=10000     #类属性,所有实例共同享有
    def say_hello(self)
        print("Hello!")

x=Person()   #创建对象
y=Person()
z=Person()   #此时print(x.money)以及y、z的money均输出10000

x.major="computer"   #创建对象后,动态添加对象特有的属性,Person类、y、z都没有此属性

Person.money=99  #此时若print(x.money)或者y,z的money,输出都是99

x.money=88    #x创建了一个实例属性,已经与Person的money无关了
              #print(x.money)返回88,而y,z的money还是99
del y         #删除对象

对象有3个特性:身份(类似于内存地址),类型,值

# 查看id()
print(id(1))
a = 1
print(id(a))
# 输出
4396235880
4396235880

1.4、构造函数和析构函数

init(self,参数2,参数3,…) #构造函数
del() #析构函数,可省略

class Person():     #创建类
    def __init__(self,money):
        self.money=money 
    def say_hello(self)
        print("hello, money=",self.money)

1.5、类方法和静态方法、类变量与实例变量

  • 类方法和静态方法
#类方法:      
class 类名:
    @classmethod   
    def 类方法名(cls,...):    #通过类名、对象名调用,可以访问类属性,不能访问对象属性
        方法体
        
#静态方法:
class 类名:
    @staticmethod
   def 类方法名():    #通过类名、对象名调用,不能直接访问类属性和实例属性但可以通过类名引用类属性
       方法体        
  • 类变量与实例变量
class A:
    i = 1               # 类变量
    def __init__(self, x, y):   # self 就是实例
        self.x = x      # 实例变量
        self.y = y
        
a = A(2, 3)
print(a.x, a.y, a.i)    # 访问顺序:先在实例变量中查找,找不到再查询类变量

A.i = 4
print(a.i)              # 输出:4,因为上面A.i = 4

a.i = 5                 # 当使用 "实例.类变量" 赋值,会新建一个变量,独立于类变量A.i
A.i = 6
print(a.i, A.i)         # 输出:5 6,当a.i=5修改a的类变量,会新建一个实例变量a.i,优先访问实例变量a.i

b = A(7, 8)
print(b.i)              # 输出:6,而不是1,因为类变量时共享的,而上面修改了类变量

1.6、公有、保护、私有变量:通过下划线数量和位置区分

xxx 公有变量
__xxx 私有变量,不允许类的外部访问
_xxx 保护变量,允许本身和子类访问
xxx 专有变量,方法

1.7、继承

class 子类名(父类名):
    类属性=初值
    方法(参数列表)

1.7.1、多继承MRO(Method Resolution Order)

MRO是用来描述多继承中查找父类的顺序。Python3中使用“C3算法”来确定查找父类的顺序。

下面2种搜索属性的方法(深度/广度搜索),都会有不合理的地方。
请添加图片描述

在Python2.2之前,类的继承是经典类的继承方式,若不显示继承object类,则不会继承object类。而Python3之后这种经典类的继承方式已经没有了。Python3之后使用了“C3算法”的搜索方法。

2、相对/绝对路径、判断路径

C:\A\B是绝对路径,如果当前路径是C:\A,则 \B是相对于当前目录,则表示C:\A\B 。而 ..\D是相对路径,相对于当前目录的父目录,则表示 C:\D

2.1、常用操作路径

符号、函数说明例子
‘\\’In [36]: os.path.exists(‘D:\\Users\\11137042’)
Out[36]: True
‘r’In [37]: os.path.exists(r’D:\Users\11137042’)
Out[37]: True
‘/’python路径可以用’/‘
’/'也表示从根目录开始
In [75]: os.path.exists(‘D:/Users/11137042’)
Out[75]: True
In [76]: os.listdir(‘/Users/11137042’) == os.listdir(‘D:/Users/11137042’)
Out[76]: True
‘./’表示从当前目录开始In [77]: os.listdir(‘./dir1’)
Out[77]: [‘dir2’]
‘../’表示从上一级目录开始In [78]: os.listdir(‘../Desktop/dir1/’)
Out[78]: [‘dir2’]
os.getcwd()返回当前目录,cwd:current working directory(当前在桌面)In [68]: os.getcwd()
Out[68]: ‘D:\Users\11137042\Desktop’
os.path.exists(path)
os.path.isfile(path)
os.path.isdir(path)
判断文件(夹)是否存在In [69]: os.path.exists(r’D:\Users\11137042\Desktop\dir1\dir2\text2.txt’) # 文件(夹)
Out[69]: True
In [81]: os.path.isfile(‘./dir1/text1.txt’) # 文件
Out[81]: True
In [82]: os.path.isdir(‘./dir1/’) # 文件夹
Out[82]: True
os.path.isabs(path)判断path是否是绝对路径(注意相对路径带’.',如.\或..\)In [73]: os.path.isabs(r’D:\Users\11137042\Desktop\dir1’),os.path.isabs(‘.\dir1’)
Out[73]: (True, False)
os.path.abspath(“./”)相对路径改为绝对路径
os.listdir(path)获取目录path中的文件及目录In [84]: os.listdir(‘./dir1’)
Out[84]: [‘dir2’, ‘text1.txt’]
os.makedirs(path)创建子目录In [85]: os.makedirs(‘./dir_test1/dir_test2’)
os.rmdir(path)
os.remove(path)
删除子目录
删除文件
In [86]: os.rmdir(‘./dir_test1/dir_test2’) # 这里只删除了dir_test2,没有删除dir_test1
sys.argv[0]获取脚本当前路径print(sys.argv[0]) # 输出:D:\Users\11137042\Desktop\tkinterTest.py
os.path.normpath()路径规范化输出In [41]: os.path.normpath(r"D:\Users\11137042\Desktop\test")
Out[41]: ‘D:\\Users\\11137042\\Desktop\test’

2.2、获取桌面路径

• 获取桌面路径:前两个方法是通过注册表来获取当前windows桌面绝对路径

方法示例
winreg.OpenKeyIn [10]: import winreg # Python3以前是: _winreg
In [11]: key = winreg.OpenKey(winreg.HKEY_CURRENT_USER,r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders")
In [12]: winreg.QueryValueEx(key, "Desktop")[0].replace("\", "/")
Out[12]: "D:/Users/11137042/Desktop"
win32扩展方法1In [14]: import win32api,win32con
In [15]: key =win32api.RegOpenKey(win32con.HKEY_CURRENT_USER,r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders",0,win32con.KEY_READ)
In [16]: win32api.RegQueryValueEx(key,"Desktop")[0].replace("\", "/")
Out[16]: "D:/Users/11137042/Desktop"
win32扩展方法2In [21]: from win32com.shell import shell, shellcon
In [22]: ilist =shell.SHGetSpecialFolderLocation(0, shellcon.CSIDL_DESKTOP)
In [23]: shell.SHGetPathFromIDList(ilist)
Out[23]: b"D:\Users\11137042\Desktop"
此法在用户改变了桌面路径后,可能会失效In [24]: import os
In [25]: os.path.join(os.path.expanduser("~"), "Desktop").replace("\\", "/")
Out[25]: "D:/Users/11137042/Desktop"
获取当前pc的主机名,一般情况下,windows系统启用第一次开始时会设置一个电脑名,这个电脑名会出现在C盘下的用户目录下,如我的电脑名是‘11137042’,那么会有一个目录路径:C:\Users\11137042,这时候我的桌面路径就是:C:\Users\11137042\Desktop(问题就是如果你随后修改了电脑名,这个方法就不生效了),代码表现的跟第四种很像下面实测失效了:
In [36]: import socket, os
In [37]: hostname = socket.gethostname() #socket.getfqdn(socket.gethostname())
In [38]: basepath = os.path.join("C:\Users\", hostname)
In [39]: os.path.join(basepath, "Desktop").replace("\", "/")
Out[39]: "C:/Users/YF-11137042/Desktop"

3、文件

打开(创建)文件:open(文件名,访问模式)

语法:open(name[, mode[, buffering]])

属性说明
name一个包含了你要访问的文件名称的字符串值。
modemode 决定了打开文件的模式:只读,写入,追加等。所有可取值见如下的完全列表。这个参数是非强制的,默认文件访问模式为只读®。
buffering如果 buffering 的值被设为 0,就不会有寄存。如果 buffering 的值取 1,访问文件时会寄存行。如果将 buffering 的值设为大于 1 的整数,表明了这就是的寄存区的缓冲大小。如果取负值,寄存区的缓冲大小则为系统默认。

访问模式:

详细参考:https://www.runoob.com/python/python-func-open.html

命令模式文件不存在时文件指针
‘r’读取模式,以只读方式打开文件报错文件开头
‘w’写入模式,以写入方式打开文件,会覆盖已经存在的文件创建文件开头
‘x’异或模式,文件存在则报错(防止覆盖)创建文件开头
‘a’追加模式,以写入方式打开文件创建在末尾追加写入
‘t’文本模式(默认)

拓展模式:

命令说明
‘+’plus,以可读写的方式打开文件
‘b’bytes,以二进制模式打开文件

可以组合使用:
w,r,a,x
wb,rb,ab,xb
w+,r+,a+,x+
wb+,rb+,ab+,xb+

  • 读取文件:
文件对象.read()     读取整个文件,换行符以\n显示 
文件对象.readline()   每次读文件一行
指定字节数:文件对象.read(字节数) 、文件对象.readline(字节数),字节数是从"文件指针"开始计数的:
# read()
In [45]: fp = open("test.txt", mode='r', encoding='utf-8')     # 以只读模式打开
In [46]: fp.read()
Out[46]: 'The Zen of Python, by Tim Peters\n\nBeautiful is better than ugly.\nExplicit is better than implicit.\nSimple is better than complex.\nComplex is better than complicated.\nFlat is better than nested.\nSparse is better than dense.\nReadability counts.\nSpecial cases aren’t special enough to break the rules.\nAlthough practicality beats purity.\nErrors should never pass silently.\nUnless explicitly silenced.\nIn the face of ambiguity, refuse the temptation to guess.\nThere should be one-- and preferably only one --obvious way to do it.\nAlthough that way may not be obvious at first unless you’re Dutch.\nNow is better than never.\nAlthough never is often better than right now.\nIf the implementation is hard to explain, it’s a bad idea.\nIf the implementation is easy to explain, it may be a good idea.\nNamespaces are one honking great idea – let’s do more of those!\n'
In [47]: fp.close()

In [51]: fp = open("test.txt", mode='r', encoding='utf-8')     # 以只读模式打开
In [52]: fp.read(10)
Out[52]: 'The Zen of'
In [53]: fp.close()

# readline()
In [60]: fp = open("test.txt", mode='r', encoding='utf-8')     # 以只读模式打开
In [61]: fp.readline(10)
Out[61]: 'The Zen of'
In [62]: fp.readline()
Out[62]: ' Python, by Tim Peters\n'
In [63]: fp.readline()
Out[63]: '\n'
In [64]: fp.close()

# write()
In [67]: fp = open("test.txt", mode='w', encoding='utf-8')     # 以写模式打开
In [68]: fp.write("hello world")
Out[68]: 11		# 返回写入的字符数
In [69]: fp.close()
In [70]: with open("test.txt", 'r') as fp:
    ...:     print(fp.read())
hello world


# r+
In [78]: # test.txt原来内容:hello world!
    ...: with open("test.txt", 'r+') as fp:
    ...:     fp.write("123")
    ...:     print(fp.read())
    ...: with open("test.txt", 'r+') as fp:
    ...:     print(fp.read())
    ...:     
lo world!
123lo world!
  • 关闭文件: 文件对象.close()
    python有垃圾回收机制,会自动关闭不再使用的文件,但最好写入后马上关闭,防止文件缓存而未写入硬盘时出现意外

4、异常处理

try:
    语句块
except 异常1 as 错误原因   #"as 错误原因"可以省略
    处理代码块
except 异常2 as 错误原因
    处理代码块
...
finally:     #无论是否异常,最后都会执行,常用于释放资源等等
     语句块
try:
	f=open('c:/test.txt)
except IOError as e:
	print(e)
finally:
	f.close()
#如果出现异常,则显示:
[Errno 2]No such file or directory:'c:/test.txt'     
  • Python中常见的异常:
IOError            输入输出异常(基本上是无法打开文件)
ImportError        无法导入模块或包(基本上是路径问题或名称错误)
Indentation Error  缩进错误;代码没有正确对齐
NameError          没有声明、或初始化对象
KeyError           试图访问字典里不存在的键
AttributeError     试图访问一个对象没有的属性
TypeError          类型不匹配
ValueError         传入一个调用者不期望的值,即使值的类型是正确的
NotImplementedError 所在的方法没有被子类重写,但是调用了就会报错

Python中的异常类是继承关系的,而Exception是所有非系统退出类异常类的基类,可以通过捕获它来避免程序遇到错误而退出:(但不建议这么做,有些错误难以预料)

try:
    语句块
except Exception as e:
	异常处理语句块    

在这里插入图片描述

5、抽象基类

  • 抽象基类:

1、所有继承抽象基类的类都必须实现抽象基类中特定的方法
2、抽象基类无法实例化

  • Python是动态类型的语言,它的变量是“没有类型”,变量名仅是一个符号,不需要指明变量的类型(因而不能编译时检查变量类型,只能在运行时才能发现语言的错误)
  • 类中实现了某种魔法函数,它的对象就是某种指定的类型,这种特性也被称为协议

5.1、什么时候使用抽象基类

其实并不是十分推荐使用抽象基类,不容易理解,用的也比较少。

5.1.1、我们希望在某些情况之下判断某个对象的类型:

from collections.abc import Sized

class Company(object):
    def __init__(self, employee_list):
        self.employee = employee_list
        
    def __len__(self):
        return len(self.employee)
    
    
com = Company(["bobby1", "bobby2"])

# 判断 Company类 的类型,更趋向于判断“该类是否属于某一类型”(isinstance),而不是判断“类中是否有某种方法”(hasttr)
print(hasattr(com, "__len__"))		# 输出True
print(isinstance(com, Sized))       # 输出True,判断对象com 是否是 类型Sized,这里是 虚基类 来实现的

com没有继承Sized,但是用isinstance(com, Sized)返回True!!主要是魔法函数__subclasshook__()的_check_methods(C, “__len__”)判断Company类是否实现了__len__()方法。

5.1.1.1、 关于Sized源码
  • typing.py:
    Sized = _alias(collections.abc.Sized, 0) # Not generic.

  • collections\abc.py:
    from _collections_abc import *

  • _collections_abc.py:
class Sized(metaclass=ABCMeta):		# ABCMeta后面元类编程会有

    __slots__ = ()

    @abstractmethod					# 可见使用虚基类实现
    def __len__(self):
        return 0

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Sized:
            return _check_methods(C, "__len__")
        return NotImplemented
  • 在Python中已经实现了一些通用的抽象基类,让我们可以了解Python数据的一些接口,它是放在collections.abc模块中:
# collections/abc.py文件:
from _collections_abc import *		# 调用 _collections_abc
from _collections_abc import __all__

# _collections_abc.py文件:
from abc import ABCMeta, abstractmethod
import sys

__all__ = ["Awaitable", "Coroutine",
           "AsyncIterable", "AsyncIterator", "AsyncGenerator",
           "Hashable", "Iterable", "Iterator", "Generator", "Reversible",
           "Sized", "Container", "Callable", "Collection",
           "Set", "MutableSet",
           "Mapping", "MutableMapping",
           "MappingView", "KeysView", "ItemsView", "ValuesView",
           "Sequence", "MutableSequence",
           "ByteString",
           ]
# 后面省略。。。

上面__all__列表中都是抽象基类,例如"Sized"(上文也有它源码),这些抽象基类很多都实现了__subclasshook__()方法 。

5.1.1.2、关于isinstance()

isinstance除了上面会用抽象基类判断子类,还会找到继承链来进行判断,而type()不能判断某个对象的类型是否继承自某个父类。

class A:
    pass

class B(A):
    pass

b = B()

print(type(b))              # 输出:<class '__main__.B'>
print(type(b) is B)         # 输出:True

print(type(b) is A)         # 输出:False,is相当于是判断函数的id(),type(b)相当于是模板类B
print(isinstance(b, A))     # 输出:True,判断继承的类型用isinstance,而不是type

5.1.2、设计一个抽象基类,使得其子类必须实现某些方法:

当子类没有重写抽象基类的方法,两种检测方法:
初始化对象不报错,函数调用时才报错

class FatherClass():
    def fun(self):
        raise NotImplementedError

class SonClass(FatherClass):
    pass
    
obj = SonClass()        # 创建对象时不会报错
obj.fun()               # 调用fun函数就会报错

# 报错:
Traceback (most recent call last):

  File "C:\Users\ZHUIAO\Desktop\untitled1.py", line 9, in <module>
    obj.fun()

  File "C:\Users\ZHUIAO\Desktop\untitled1.py", line 3, in fun
    raise NotImplementedError

NotImplementedError

初始化就开始报错:

import abc

class FatherClass(metaclass=abc.ABCMeta):   # metaclass在元类编程中介绍
    
    @abc.abstractmethod()
    def fun(self):
        pass

class SonClass(FatherClass):
    pass
    
obj = SonClass()        # 创建对象时就会报错
# obj.fun()

# 输出:
Traceback (most recent call last):

  File "C:\Users\ZHUIAO\Desktop\untitled1.py", line 3, in <module>
    class FatherClass(metaclass=abc.ABCMeta):   # metaclass在元类编程中介绍

  File "C:\Users\ZHUIAO\Desktop\untitled1.py", line 5, in FatherClass
    @abc.abstractmethod()

TypeError: abstractmethod() missing 1 required positional argument: 'funcobj'
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值