一、模块介绍
1、什么是模块?
模块就是一系列功能的集合体,分为三大类
I:内置的模块
II:第三方的模块
III:自定义的模块
一个python文件本身就一个模块,文件名m.py,模块名叫m
ps:模块有四种形式
1 使用python编写的.py文件
2 已被编译为共享库或DLL的C或C++扩展
3 把一系列模块组织到一起的文件夹
(注:文件夹下有一个__init__.py文件,该文件夹称之为包)
4 使用C编写并链接到python解释器的内置模块
2、为何有用模块
I:内置与第三的模块拿来就用,无需定义,这种拿来主义,可以极大地提升自己的开发效率
II:自定义的模块
可以将程序的各部分功能提取出来放到一模块中为大家共享使用
好处是减少了代码冗余,程序组织结构更加清晰
3、如何用模块
'''
y=333
z=444
import foo
# 1、首次导入模块会发生3件事
# 1、执行foo.py
# 2、产生foo.py的名称空间,将foo.py运行过程中产生的名字都丢到foo的名称空间中
# 3、在当前文件中产生的有一个名字foo,该名字指向2中产生的名称空间
# 之后的导入,都是直接引用首次导入产生的foo.py名称空间,不会重复执行代码
# import foo
# import foo
# import foo
# import foo
# 2、引用:
# print(foo.x)
# print(foo.get)
# print(foo.change)
# 强调1:模块名.名字,是指名道姓地问某一个模块要名字对应的值,不会与当前名称空间中的名字发生冲突
# x=1111111111111
# print(x)
# print(foo.x)
# 强调2:无论是查看还是修改操作的都是模块本身,与调用位置无关
# import foo
#
# x=3333333333
# # foo.get()
#
# foo.change()
# print(x)
#
# print(foo.x)
# foo.get()
# 3、可以以逗号为分隔符在一行导入多个模块
# 建议如下所示导入多个模块
# import time
# import foo
# import m
# 不建议在一行同时导入多个模块
import time,foo,m
# 4、导入模块的规范
#I. python内置模块
#II. 第三方模块
#III. 程序员自定义模块
# import time
# import sys
#
# import 第三方1
# import 第三方2
#
# import 自定义模块1
# import 自定义模块2
# import 自定义模块3
# 5、import 。。。 as 。。。
# import foo as f # f=foo
# f.get()
# import abcdefgadfadfas
# #
# # abcdefgadfadfas.f1
# # abcdefgadfadfas.f2
# # abcdefgadfadfas.f3
# import abcdefgadfadfas as mmm
#
# mmm.f1
# mmm.f2
# mmm.f3
#6、模块是第一类对象
import foo
#7、自定义模块的命名应该采用纯小写+下划线的风格
#8、可以在函数内导入模块
def func():
import foo
二、一个py文件有几种用途
# 一个python文件有两种用途
# 1、被当成程序运行
# 2、被当做模块导入
# 二者的区别是什么?
三、from...import导入模块
# impot导入模块在使用时必须加前缀"模块."
# 优点:肯定不会与当前名称空间中的名字冲突
# 缺点:加前缀显得麻烦
# from ... import ...导入也发生了三件事
# 1、产一个模块的名称空间
# 2、运行foo.py将运行过程中产生的名字都丢到模块的名称空间去
# 3、在当前名称空间拿到一个名字,该名字与模块名称空间中的某一个内存地址
# from foo import x # x=模块foo中值0的内存地址
# from foo import get
# from foo import change
# print(x)
# print(get)
# print(change)
# x=333333333
# print(x)
# get()
# change()
# get()
# print(x)
# from foo import x # x=新地址
# print(x)
# from...impot...导入模块在使用时不用加前缀
# 优点:代码更精简
# 缺点:容易与当前名称空间混淆
# from foo import x # x=模块foo中值1的内存地址
# x=1111
# 一行导入多个名字(不推荐)
# from foo import x,get,change
# *:导入模块中的所有名字
# name='egon'
# from foo import *
# print(name)
from socket import *
# 了解:__all__
# from foo import *
# print(x)
# print(get)
# print(change)
# 起别名
from foo import get as g
print(g)
四、模块的搜索路径优先级
# 无论是import还是from...import在导入模块时都涉及到查找问题
# 优先级:
# 1、内存(内置模块)
# 2、硬盘:按照sys.path中存放的文件的顺序依次查找要导入的模块
# import sys
# 值为一个列表,存放了一系列的对文件夹
# 其中第一个文件夹是当前执行文件所在的文件夹
# print(sys.path)
# import foo # 内存中已经有foo了
# foo.say()
#
# import time
# time.sleep(10)
#
# import foo
# foo.say()
# 了解:sys.modules查看已经加载到内存中的模块
import sys
# import foo # foo=模块的内存地址
# del foo
# def func():
# import foo # foo=模块的内存地址
#
# func()
#
# # print('foo' in sys.modules)
# print(sys.modules)
import sys
# 找foo.py就把foo.py的文件夹添加到环境变量中
sys.path.append(r'/Users/linhaifeng/PycharmProjects/s14/day21/aa')
# import foo
# foo.say()
from foo import say
五、函数的类型提示
# str int ('play','music')
# def register(name:str,age:int,hobbbies:tuple)->int:
# print(name)
# print(age)
# print(hobbbies)
# return 111
#
# # register(1,'aaa',[1,])
# res=register('egon',18,('play','music'))
# def register(name:str='egon',age:int=18,hobbbies:tuple=(1,2))->int:
# print(name)
# print(age)
# print(hobbbies)
# return 111
#
# # register(1,'aaa',[1,])
# # res=register('egon',18,('play','music'))
# res=register()
def register(name:"必须传入名字傻叉",age:1111111,hobbbies:"必须传入爱好元组")->"返回的是整型":
print(name)
print(age)
print(hobbbies)
return 111
# register(1,'aaa',[1,])
# res=register('egon',18,('play','music'))
# res=register('egon',19,(1,2,3))
print(register.__annotations__)
六、包的使用
'''
1、包就是一个包含有__init__.py文件的文件夹
2、为何要有包
包的本质是模块的模块的一种形式,包是用来被当做模块导入
'''
#1、产生一个名称空间
#2、运行包下的__init__.py文件,将运行过程中产生的名字都丢到1的名称空间中
#3、在当前执行文件的名称空间中拿到一个名字mmm,mmm指向1的名称空间
# import mmm
# print(mmm.x)
# print(mmm.y)
# mmm.say()
# from mmm import x
# 模块的使用者:egon老湿
# 环境变量是以执行文件为准备的,所有的被导入的模块或者说后续的其他文件引用
# 的sys.path都是参照执行文件的sys.path
import sys
sys.path.append('/aaaaaaaaaaaaaaaaaaaaaaaaaa')
# print(sys.path)
sys.path.append(r'/Users/linhaifeng/PycharmProjects/s14/day21/aa')
# import foo # foo下__init__.py
# #
# #
# foo.f1()
# foo.f2()
# foo.f3()
# from foo import f1,f2,f3,f4
# f1()
# f2()
# f3()
# f4()
# import foo
# foo.f4()
# 强调:
# 1.关于包相关的导入语句也分为import和from ... import ...
# 两种,但是无论哪种,无论在什么位置,在导入时都必须遵循一个原则:
# 凡是在导入时带点的,点的左边都必须是一个包,否则非法。
# 可以带有一连串的点,如import 顶级包.子包.子模块,但都必须遵循这个原则。但对于导入后,在使用时就没有这种限制了,点的左边可以是包,模块,函数,类(它们都可以用点的方式调用自己的属性)。
# 例如:
# from a.b.c.d.e.f import xxx
# import a.b.c.d.e.f
# 其中a、b、c、d、e 都必须是包
# 2、包A和包B下有同名模块也不会冲突,如A.a与B.a来自俩个命名空间
#
# 3、import导入文件时,产生名称空间中的名字来源于文件,
# import 包,产生的名称空间的名字同样来源于文件,即包下的__init__.py,导入包本质就是在导入该文件
# import foo
# # print(foo.f1)
# # print(foo.f2)
# # print(foo.f3)
# # print(foo.f4)
#
# foo.f4()
# from foo import *
# print(f1)
# print(f2)
# print(f3)
# print(f4)
七、时间模块
# 时间模块优先掌握的操作
#一:time
import time
# 时间分为三种格式:
# 1、时间戳:从1970年到现在经过的秒数
# 作用:用于时间间隔的计算
# print(time.time())
# 2、按照某种格式显示的时间:2020-03-30 11:11:11
# 作用:用于展示时间
# print(time.strftime('%Y-%m-%d %H:%M:%S %p'))
# print(time.strftime('%Y-%m-%d %X'))
# 3、结构化的时间
# 作用:用于单独获取时间的某一部分
# res=time.localtime()
# print(res)
# print(res.tm_year)
# print(res.tm_yday)
#二:datetime
import datetime
# print(datetime.datetime.now())
# print(datetime.datetime.now() + datetime.timedelta(days=3))
# print(datetime.datetime.now() + datetime.timedelta(weeks=1))
# 时间模块需要掌握的操作
# 1、时间格式的转换
# struct_time->时间戳
import time
# s_time=time.localtime()
# print(time.mktime(s_time))
# 时间戳->struct_time
# tp_time=time.time()
# print(time.localtime(tp_time))
# 补充:世界标准时间与本地时间
# print(time.localtime())
# print(time.gmtime()) # 世界标准时间,了解
# print(time.localtime(333333333))
# print(time.gmtime(333333333))
# struct_time->格式化的字符串形式的时间
# s_time=time.localtime()
# print(time.strftime('%Y-%m-%d %H:%M:%S',s_time))
# print(time.strptime('1988-03-03 11:11:11','%Y-%m-%d %H:%M:%S'))
# !!!真正需要掌握的只有一条:format string<------>timestamp
# '1988-03-03 11:11:11'+7
# format string--->struct_time--->timestamp
# struct_time=time.strptime('1988-03-03 11:11:11','%Y-%m-%d %H:%M:%S')
# timestamp=time.mktime(struct_time)+7*86400
# print(timestamp)
# format string<---struct_time<---timestamp
# res=time.strftime('%Y-%m-%d %X',time.localtime(timestamp))
# print(res)
# time.sleep(3)
# 了解知识
# import time
# print(time.asctime())
import datetime
# print(datetime.datetime.now())
# print(datetime.datetime.utcnow())
print(datetime.datetime.fromtimestamp(333333))
八、random模块
import random
# print(random.random()) #(0,1)----float 大于0且小于1之间的小数
# print(random.randint(1, 3)) # [1,3] 大于等于1且小于等于3之间的整数
# print(random.randrange(1, 3)) # [1,3) 大于等于1且小于3之间的整数
#
# print(random.choice([111, 'aaa', [4, 5]])) # 1或者23或者[4,5]
#
# print(random.sample([111, 'aaa', 'ccc','ddd'],2)) # 列表元素任意2个组合
#
# print(random.uniform(1, 3)) # 大于1小于3的小数,如1.927109612082716
#
# item = [1, 3, 5, 7, 9]
# random.shuffle(item) # 打乱item的顺序,相当于"洗牌"
# print(item)
# 应用:随机验证码
# import random
#
# res=''
# for i in range(6):
# 从26大写字母中随机取出一个=chr(random.randint(65,90))
# 从10个数字中随机取出一个=str(random.randint(0,9))
#
# 随机字符=random.choice([从26大写字母中随机取出一个,从10个数字中随机取出一个])
# res+=随机字符
import random
def make_code(size=4):
res=''
for i in range(size):
s1=chr(random.randint(65,90))
s2=str(random.randint(0,9))
res+=random.choice([s1,s2])
return res
print(make_code(6))
九、OS模块
import os
# 获取某一个文件夹下所有的子文件以及子文件夹的名字
# res=os.listdir('.')
# print(res)
#
#
# size=os.path.getsize(r'/Users/linhaifeng/PycharmProjects/s14/day22/01 时间模块.py')
# print(size)
# os.remove() 删除一个文件
# os.rename("oldname","newname") 重命名文件/目录
# 应用程序----》"ls /"
# os.system("ls /")
# 规定:key与value必须都为字符串
# os.environ['aaaaaaaaaa']='111'
# print(os.environ)
# print(os.path.dirname(r'/a/b/c/d.txt'))
# print(os.path.basename(r'/a/b/c/d.txt'))
# print(os.path.isfile(r'笔记.txt'))
# print(os.path.isfile(r'aaa'))
# print(os.path.isdir(r'aaa'))
# print(os.path.join('a','/','b','c','d'))
# 推荐用这种
BASE_DIR=os.path.dirname(os.path.dirname(__file__))
# print(BASE_DIR)
# BASE_DIR=os.path.normpath(os.path.join(
# __file__,
# '..',
# '..'
# ))
# print(BASE_DIR)
# 在python3.5之后,推出了一个新的模块pathlib
from pathlib import Path
# res = Path(__file__).parent.parent
# print(res)
# res=Path('/a/b/c') / 'd/e.txt'
# print(res)
# print(res.resolve())
10、sys模块
sys模块的常见函数列表
sys.argv: 实现从程序外部向程序传递参数。
sys.exit([arg]): 程序中间的退出,arg=0为正常退出。
sys.getdefaultencoding(): 获取系统当前编码,一般默认为ascii。
sys.setdefaultencoding(): 设置系统默认编码,执行dir(sys)时不会看到这个方法,在解释器中执行不通过,可以先执行reload(sys),在执行 setdefaultencoding('utf8'),此时将系统默认编码设置为utf8。(见设置系统默认编码 )
sys.getfilesystemencoding(): 获取文件系统使用编码方式,Windows下返回'mbcs',mac下返回'utf-8'.
sys.path: 获取指定模块搜索路径的字符串集合,可以将写好的模块放在得到的某个路径下,就可以在程序中import时正确找到。
sys.platform: 获取当前系统平台。
sys.stdin,sys.stdout,sys.stderr: stdin , stdout , 以及stderr 变量包含与标准I/O 流对应的流对象. 如果需要更好地控制输出,而print 不能满足你的要求, 它们就是你所需要的. 你也可以替换它们, 这时候你就可以重定向输出和输入到其它设备( device ), 或者以非标准的方式处理它们
十一、 shutil模块
# 1、什么是序列化&反序列化
# 内存中的数据类型---->序列化---->特定的格式(json格式或者pickle格式)
# 内存中的数据类型<----反序列化<----特定的格式(json格式或者pickle格式)
# 土办法:
# {'aaa':111}--->序列化str({'aaa':111})----->"{'aaa':111}"
# {'aaa':111}<---反序列化eval("{'aaa':111}")<-----"{'aaa':111}"
# 2、为何要序列化
# 序列化得到结果=>特定的格式的内容有两种用途
# 1、可用于存储=》用于存档
# 2、传输给其他平台使用=》跨平台数据交互
# python java
# 列表 特定的格式 数组
# 强调:
# 针对用途1的特定一格式:可是一种专用的格式=》pickle只有python可以识别
# 针对用途2的特定一格式:应该是一种通用、能够被所有语言识别的格式=》json
# 3、如何序列化与反序列化
# 示范1
# import json
# # 序列化
# json_res=json.dumps([1,'aaa',True,False])
# # print(json_res,type(json_res)) # "[1, "aaa", true, false]"
#
# # 反序列化
# l=json.loads(json_res)
# print(l,type(l))
# 示范2:
import json
# 序列化的结果写入文件的复杂方法
# json_res=json.dumps([1,'aaa',True,False])
# # print(json_res,type(json_res)) # "[1, "aaa", true, false]"
# with open('test.json',mode='wt',encoding='utf-8') as f:
# f.write(json_res)
# 将序列化的结果写入文件的简单方法
# with open('test.json',mode='wt',encoding='utf-8') as f:
# json.dump([1,'aaa',True,False],f)
# 从文件读取json格式的字符串进行反序列化操作的复杂方法
# with open('test.json',mode='rt',encoding='utf-8') as f:
# json_res=f.read()
# l=json.loads(json_res)
# print(l,type(l))
# 从文件读取json格式的字符串进行反序列化操作的简单方法
# with open('test.json',mode='rt',encoding='utf-8') as f:
# l=json.load(f)
# print(l,type(l))
# json验证: json格式兼容的是所有语言通用的数据类型,不能识别某一语言的所独有的类型
# json.dumps({1,2,3,4,5})
# json强调:一定要搞清楚json格式,不要与python混淆
# l=json.loads('[1, "aaa", true, false]')
# l=json.loads("[1,1.3,true,'aaa', true, false]")
# print(l[0])
# 了解
# l = json.loads(b'[1, "aaa", true, false]')
# print(l, type(l))
# with open('test.json',mode='rb') as f:
# l=json.load(f)
# res=json.dumps({'name':'哈哈哈'})
# print(res,type(res))
# res=json.loads('{"name": "\u54c8\u54c8\u54c8"}')
# print(res,type(res))
# 4、猴子补丁
# 在入口处打猴子补丁
# import json
# import ujson
#
# def monkey_patch_json():
# json.__name__ = 'ujson'
# json.dumps = ujson.dumps
# json.loads = ujson.loads
#
# monkey_patch_json() # 在入口文件出运行
# import ujson as json # 不行
# 后续代码中的应用
# json.dumps()
# json.dumps()
# json.dumps()
# json.dumps()
# json.dumps()
# json.dumps()
# json.dumps()
# json.dumps()
# json.loads()
# json.loads()
# json.loads()
# json.loads()
# json.loads()
# json.loads()
# json.loads()
# json.loads()
# json.loads()
# json.loads()
# json.loads()
# 5.pickle模块
import pickle
# res=pickle.dumps({1,2,3,4,5})
# print(res,type(res))
# s=pickle.loads(res)
# print(s,type(s))
十二、json&pickle模块
import configparser
config=configparser.ConfigParser()
config.read('test.ini')
# 1、获取sections
# print(config.sections())
# 2、获取某一section下的所有options
# print(config.options('section1'))
# 3、获取items
# print(config.items('section1'))
# 4、
# res=config.get('section1','user')
# print(res,type(res))
# res=config.getint('section1','age')
# print(res,type(res))
# res=config.getboolean('section1','is_admin')
# print(res,type(res))
# res=config.getfloat('section1','salary')
# print(res,type(res))
十三、shelve模块
十四、xml模块
十五、configparser模块
十六、hashlib模块
# 1、什么是哈希hash
# hash一类算法,该算法接受传入的内容,经过运算得到一串hash值
# hash值的特点:
#I 只要传入的内容一样,得到的hash值必然一样
#II 不能由hash值返解成内容
#III 不管传入的内容有多大,只要使用的hash算法不变,得到的hash值长度是一定
# 2、hash的用途
# 用途1:特点II用于密码密文传输与验证
# 用途2:特点I、III用于文件完整性校验
# 3、如何用
# import hashlib
#
# m=hashlib.md5()
# m.update('hello'.encode('utf-8'))
# m.update('world'.encode('utf-8'))
# res=m.hexdigest() # 'helloworld'
# print(res)
#
# m1=hashlib.md5('he'.encode('utf-8'))
# m1.update('llo'.encode('utf-8'))
# m1.update('w'.encode('utf-8'))
# m1.update('orld'.encode('utf-8'))
# res=m1.hexdigest()# 'helloworld'
# print(res)
# 模拟撞库
# cryptograph='aee949757a2e698417463d47acac93df'
# import hashlib
#
# # 制作密码字段
# passwds=[
# 'alex3714',
# 'alex1313',
# 'alex94139413',
# 'alex123456',
# '123456alex',
# 'a123lex',
# ]
#
# dic={}
# for p in passwds:
# res=hashlib.md5(p.encode('utf-8'))
# dic[p]=res.hexdigest()
#
# # 模拟撞库得到密码
# for k,v in dic.items():
# if v == cryptograph:
# print('撞库成功,明文密码是:%s' %k)
# break
# 提升撞库的成本=>密码加盐
import hashlib
m=hashlib.md5()
m.update('天王'.encode('utf-8'))
m.update('alex3714'.encode('utf-8'))
m.update('盖地虎'.encode('utf-8'))
print(m.hexdigest())
# m.update(文件所有的内容)
# m.hexdigest()
#
# f=open('a.txt',mode='rb')
# f.seek()
# f.read(2000) # 巨琳
# m1.update(文见的一行)
#
# m1.hexdigest()
十七、suprocess模块
import subprocess
obj=subprocess.Popen('echo 123 ; ls / ; ls /root',shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
# print(obj)
# res=obj.stdout.read()
# print(res.decode('utf-8'))
err_res=obj.stderr.read()
print(err_res.decode('utf-8'))
十八、 logging模块
import logging
logging.basicConfig(
# 1、日志输出位置:1、终端 2、文件
filename='access.log', # 不指定,默认打印到终端
# 2、日志格式
format='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s',
# 3、时间格式
datefmt='%Y-%m-%d %H:%M:%S %p',
# 4、日志级别
# critical => 50
# error => 40
# warning => 30
# info => 20
# debug => 10
level=10,
)
logging.debug('调试debug') # 10
logging.info('消息info') # 20
logging.warning('警告warn')# 30
logging.error('egon提现失败') # 40
logging.critical('严重critical') # 50
十九、re模块
import re
print(re.findall('\w','aAbc123_*()-='))
print(re.findall('\W','aAbc123_*()-= '))
print(re.findall('\s','aA\rbc\t\n12\f3_*()-= '))
print(re.findall('\S','aA\rbc\t\n12\f3_*()-= '))
print(re.findall('\d','aA\rbc\t\n12\f3_*()-= '))
print(re.findall('\D','aA\rbc\t\n12\f3_*()-= '))
print(re.findall('\D','aA\rbc\t\n12\f3_*()-= '))
print(re.findall('\Aalex',' alexis alex sb'))
# alex
# print(re.findall('sb\Z',' alexis alexsb sb'))
# sb\Z
print(re.findall('sb\Z',"""alex
alexis
alex
sb
"""))
# print(re.findall('^alex','alexis alex sb'))
# print(re.findall('sb$','alexis alex sb'))
# print(re.findall('sb$',"""alex
# alexis
# alex
# sb
# """))
# print(re.findall('^alex$','alexis alex sb'))
# print(re.findall('^alex$','al ex'))
# print(re.findall('^alex$','alex'))
# 重复匹配:| . | * | ? | .* | .*? | + | {n,m} |
# 1、.:匹配除了\n之外任意一个字符,指定re.DOTALL之后才能匹配换行符
# print(re.findall('a.b','a1b a2b a b abbbb a\nb a\tb a*b'))
# a.b
# ['a1b','a2b','a b','abb','a\tb','a*b']
# print(re.findall('a.b','a1b a2b a b abbbb a\nb a\tb a*b',re.DOTALL))
# 2、*:左侧字符重复0次或无穷次,性格贪婪
# print(re.findall('ab*','a ab abb abbbbbbbb bbbbbbbb'))
# ab*
#['a','ab','abb','abbbbbbbb']
# 3、+:左侧字符重复1次或无穷次,性格贪婪
# print(re.findall('ab+','a ab abb abbbbbbbb bbbbbbbb'))
# ab+
# 4、?:左侧字符重复0次或1次,性格贪婪
# print(re.findall('ab?','a ab abb abbbbbbbb bbbbbbbb'))
# ab?
# ['a','ab','ab','ab']
# 5、{n,m}:左侧字符重复n次到m次,性格贪婪
# {0,} => *
# {1,} => +
# {0,1} => ?
# {n}单独一个n代表只出现n次,多一次不行少一次也不行
# print(re.findall('ab{2,5}','a ab abb abbb abbbb abbbbbbbb bbbbbbbb'))
# ab{2,5}
# ['abb','abbb','abbbb','abbbbb]
# print(re.findall('\d+\.?\d*',"asdfasdf123as1111111.123dfa12adsf1asdf3"))
# \d+\.?\d* \d+\.?\d+
# []匹配指定字符一个
# print(re.findall('a\db','a1111111b a3b a4b a9b aXb a b a\nb',re.DOTALL))
# print(re.findall('a[501234]b','a1111111b a3b a4b a9b aXb a b a\nb',re.DOTALL))
# print(re.findall('a[0-5]b','a1111111b a3b a1b a0b a4b a9b aXb a b a\nb',re.DOTALL))
# print(re.findall('a[0-9a-zA-Z]b','a1111111b axb a3b a1b a0b a4b a9b aXb a b a\nb',re.DOTALL))
#
# print(re.findall('a[^0-9a-zA-Z]b','a1111111b axb a3b a1b a0b a4b a9b aXb a b a\nb',re.DOTALL))
# print(re.findall('a-b','a-b aXb a b a\nb',re.DOTALL))
print(re.findall('a[-0-9\n]b','a-b a0b a1b a8b aXb a b a\nb',re.DOTALL))
二十、类型提示 Type hinting
类型提示 Type hinting
最低 Python 版本为 3.5
from pathlib import Path
root=Path(__file__)
res=root.parent.parent / r'\bbb\aaa\ccc'
print(res)
print(res.resolve())