S9-魔方方法与模块

S8-魔方方法与模块

  • 8 魔方方法和模块
    • 8.1 魔方方法
      • 8.1.1 基本的魔方方法
      • 8.1.2 算术运算符
      • 8.1.3 反算术运算符
      • 8.1.4 增量赋值运算符
      • 8.1.5 一元运算符
      • 8.1.6 属性访问
      • 8.1.7 描述符
      • 8.1.8 定制序列
      • 8.1.9 迭代器
      • 8.1.10 生成器
      • 8.1.11 【练习题】
    • 8.2 模块
      • 8.2.1 什么是模块?
      • 8.2.2 命名空间-NameSpace
      • 8.2.3 导入模块
      • 8.2.4 if __name__ == '__main__'
      • 8.2.4 搜索路径
      • 8.2.6 Package(包)
      • 8.2.7 【练习题】

本章节主要知识点是:魔方方法和模块

8 魔方方法和模块

8.1 魔方方法

魔方方法总是被双下划线包围,例如__init__。魔方方法是面向对象的python的一切。
魔方方法的“魔力”体现在他们总能够在适当的时候被自动调用。

  • 魔方方法的第一个参数应为cls(类方法)或者(实例方法):cls–指向自己的类 self–指向自己的一个实例对象

8.1.1 基本的魔方方法

  • init(self,[…])构造器,当一个实例被创建的时候调用的初始化方法。
    【例子】
class  Rectangle:
	def  __init__(self,x,y):
		self.x = x
		self.y = y
	def  getPeri(self):
		return (self.x+self.y) * 2
	def  getArea(self):
		return self.x * self.y

rect = Rectangle(4,5)
print(rect.getPeri())	# 18
print(rect.getArea)	# 20
  • new(cls,[…])在一个对象实例化的时候所调用的第一个方法,在调用__init__初始化前,先调用__new__。
  1. new__至少要有一个参数cls,代表要实例化的类,此参数在实例化时由python解释器自动提供,后面的参数直接传递给__init
  2. new__对当前类进行了实例化,并将实例返回,传给__init__的self。但是,执行了__new,并不一定会进入__init__,只有__new__返回了,当前的类cls的实例,当前类的__init__才会进入。
    【例子】
class  A(object):
	def __init__(self,value):
		print("into A__init__")
		self.value = value

	def __new__(cls,*args,**kwargs):
		print("into A __new__")
		print("cls")
		return object.__new__(cls)

class  B(A):
	def __init__(self,value):
		print("into B __init__")
		self.value = value
	
	def __new__(cls,*args,**kwargs):
		print("into B __new__")
		print(cls)
		return super().__new__(cls,*args,**kwargs)

b = B(10)
# into B __new__
# 
# into A __new__
# 
# into B __init__
## 但是不知道怎么做;

class A(object):
	def __init__(self,value):
		print("into A __init__")
		self.value = value
	
	def __new__(cls,*args,**kwargs):
		print("into A __new__")
		print(cls)
		return object.__new__(cls)

class B(A):
	def __init__(self,value):
		print("into B __init__")
		self.value = value
	
	def __new__(cls,*args,**kwargs):
		print("into B __new__")
		print(cls)
		return super().__new__(A,*args,**kwargs) 	 # 改动了cls变为A

b = B(10)

# into B __new__
# 
# into A __new__
# 
  • 若__new__没有正确返回当前类cls的实例,那__init__是不会被调用的,即使是父类的实例也不行,将没有__init__被调用。

【例子】利用__new__实现单例模式

class Earth:
	pass

a = Earth()
print(id(a))	# 260728291456

b = Earth(0
print(id(b))	# 260728291624

class Earth:
	__instance = None	# 定义一个类属性做判断
	
	def __new__(cls):
		if cls.__instance is None:
			cls.__instance = object.__new__(cls)
			return cls.__instance
		else:
			cls.__instance

a = Earth()
print(id(a))	# 512320401648
b = Earth()
print(id(b))  # 512320401648
  • __new__方法主要是当你继承一些不可变的class时(比如int,str,tuple),提供给你一个自定义这些类的实例化过程的途径。
    【例子】
class CapStr(str):
	def __new__(cls,string):
		string = string.upper()
		return str.__new_(cls,string)

a = CapStr("i love lsgogroup")
print(a)	# I LOVE LSOGROUP

  • del(self)析构器,当一个对象将要被系统回收之时调用的方法
  1. Python 采用自动引用计数(ARC)方式来回收对象所占用的空间,当程序中有一个变量引用该 Python 对象时,Python 会自动保证该对象引用计数为 1;当程序中有两个变量引用该 Python 对象时,Python 会自动保证该对象引用计数为 2,依此类推,如果一个对象的引用计数变成了 0,则说明程序中不再有变量引用该对象,表明程序不再需要该对象,因此 Python 就会回收该对象。

  2. 大部分时候,Python 的 ARC 都能准确、高效地回收系统中的每个对象。但如果系统中出现循环引用的情况,比如对象 a 持有一个实例变量引用对象 b,而对象 b 又持有一个实例变量引用对象 a,此时两个对象的引用计数都是 1,而实际上程序已经不再有变量引用它们,系统应该回收它们,此时 Python 的垃圾回收器就可能没那么快,要等专门的循环垃圾回收器(Cyclic Garbage Collector)来检测并回收这种引用循环。

【例子】

class  C(object):
	def __init__(self):
		print('into C __init__')	
	
	def __del__(self):
		print("into C__del__")

c1 = C()
# into C __init__
c2 = c1
c3 = c2
del c3 
del c2
del c1
# into C __del__
  • str(self):
    1. 当你打印一个对象的时候,触发__str__
    2. 当你使用 %s 格式化的时候,触发 str
    3. 当使用str强制数据类型转换的时候,触发__str__
  • repr(self):
    1. repr是str 的备胎
    2. 有__str__的时候执行__str__,没有实现__str__的时候,执行__repr__
    3. repr(obj)内置函数对应的结果是__repr__的返回值
    4. 当你使用%r格式化的时候,触发__repr__
      【例子】
class Cat:
	""" 定义一个猫类 """
	def __init__(self,new_name,new_age):
		""" 在创建完对象之后,会自动调用,它完成对象的初始化的功能"""
		self.name = new_name
		self.age = new_age
	def __str__(self):
		"""返回一个对象的描述信息"""
		return  "名字是:%s,年龄是:%d"	%(self.name,self.age)	
		
	def __repr__(self):
		"""返回一个对象的描述信息"""
		return "Cat:(%s,%d)" %(self.name,self.age)
	def eat(self):
		print("%s在吃鱼..." % self.name)
	def drink(self):
		print("%s在喝可乐..." % self.name)
	def introduce(slef):
		print("名字是:%s,年龄是:%d " %(self.name,self.age))

# 创建了一个对象
tom = Cat("汤姆",30)
print(tom)	# 名字是:汤姆,年龄是:30
print(str(tom))	# 名字是:汤姆,年龄是:30
print(repr(tom))	#  Cat:(汤姆,30)

tom.eat()	# 汤姆在吃鱼...
tom.introduce()	# 名字是:汤姆,年龄是:30

str(self)的返回结果代码可读性强。即__str__ 的意义是得到便于人们阅读的信息,就像下面的 ‘2019-10-11’ 一样。
repr(self) 的返回结果应更准确。怎么说,repr 存在的目的在于调试,便于开发者使用。
【例子】

import  datatime
today = datatime.date.today()
print(str(today))	# 2019-10-11
print(repr(today)) 	# datetime.date(2019,10,11)
print("%s " %today)	# 2019-10-11
print("%r" %today)	# datetime.date(2019,10,11)

8.1.2 算术运算符

类型工厂函数,指的是“不通过类而通过函数来创建对象”
【例子】

class  C:
	pass

print(type(len))	# 
print(type(len))  # 
print(type(dir))  # 
print(type(int))  # 
print(type(list))  # 
print(type(tuple))  # 
print(type(C))  # 
print(int('123'))  # 123

# 这个例子中list工厂函数把一个元组对象加工成了一个列表对象
print(list((1,2,3)))		# [1,2,3]
  • add(self,other)定义加法的行为:+
  • sub(self,other)定义减法的行为:-
    【例子】
class  MyClass:
	def  __init__(self,height,weight):
		self.height = height
		self.weight = weight
	
	# 两个对象的长相加,宽不变,返回一个新的类
	def __add__(self,others):
		return MyClass(self.height+others.height, self.weight-others.weight)
	# 两个对象的宽相减,长不变.返回一个新的类
	def  __sub__(self,others):
		return MyClass(self.height-others.height,self.weight-others.weight)
	
	# 输出自己的参数
	def intro(self):
		print("Height: ", self.height,"Weight: ",self.weight)

def  main():
	a = MyClass(10,5)
	a. intro
	
	b = MyClass(20,10)
	b.intro
	
	c = b-a
	c.intro()
	
	d = a+b
	d.intro()

if  __name__ == "__main__":
	main() 

# 高为 10  重为 5
# 高为 20  重为 10
# 高为 10  重为 5
# 高为 30  重为 15

mul(self, other)定义乘法的行为:*
truediv(self, other)定义真除法的行为:/
floordiv(self, other)定义整数除法的行为://
mod(self, other) 定义取模算法的行为:%
divmod(self, other)定义当被 divmod() 调用时的行为
divmod(a, b)把除数和余数运算结果结合起来,返回一个包含商和余数的元组(a // b, a % b)。
【例子】

print(divmod(7,2))	# (3,1)
print(divmod(8,2))	# (4,0)

8.1.3 反算术运算符

反运算魔方方法,与算术运算符保持一一对应,不同之处就是反运算的魔方方法多了一个"r"。
当文件做操作不支持相应的操作时被调用。
radd(self, other)定义加法的行为:+
rsub(self, other)定义减法的行为:-
rmul(self, other)定义乘法的行为:*
rtruediv(self, other)定义真除法的行为:/
rfloordiv(self, other)定义整数除法的行为://
rmod(self, other) 定义取模算法的行为:%
rdivmod(self, other)定义当被 divmod() 调用时的行为
rpow(self, other[, module])定义当被 power() 调用或 ** 运算时的行为
rlshift(self, other)定义按位左移位的行为:<<
rrshift(self, other)定义按位右移位的行为:>>
rand(self, other)定义按位与操作的行为:&
rxor(self, other)定义按位异或操作的行为:^
ror(self, other)定义按位或操作的行为:

a + b
这里的加数是a,被加数是b,因此是a主动,,反运算就是如果a对象的__add__()方法没有实现或者不支持相应的操作,那么python就会调用b的__radd__()方法。
【例子】

class Nint(int):
	def __radd__(self,other):
		return int.__sub__(other,self)	#注意self在后面

a = Nint(5)
b = Nint(3)
print(a + b)  # 8
print(1 + b)  # -2
#  结果没有看懂没有看懂

8.1.4 增量赋值运算符

iadd(self, other)定义赋值加法的行为:+=
isub(self, other)定义赋值减法的行为:-=
imul(self, other)定义赋值乘法的行为:*=
itruediv(self, other)定义赋值真除法的行为:/=
ifloordiv(self, other)定义赋值整数除法的行为://=
imod(self, other)定义赋值取模算法的行为:%=
ipow(self, other[, modulo])定义赋值幂运算的行为:**=
ilshift(self, other)定义赋值按位左移位的行为:<<=
irshift(self, other)定义赋值按位右移位的行为:>>=
iand(self, other)定义赋值按位与操作的行为:&=
ixor(self, other)定义赋值按位异或操作的行为:^=
ior(self, other)定义赋值按位或操作的行为:|=

8.1.5 一元运算符

neg(self)定义正号的行为:+x
pos(self)定义负号的行为:-x
abs(self)定义当被abs()调用的时候
invert(self)定义按位求反的行为:~x

8.1.6 属性访问

getattr(self,name):定义当用户试图获取一个不存在的属性的行为;
getattribute(self,name):定义当该类的属性被访问的时候(先调用该方法,查看是否存在该属性,若不存在,接着去调用__getattr__)。
setattr(self,name,value):定义当一个属性被设置时的行为
delattr(self,name): 定义当一个属性被删除时的行为
【例子】

class C:
	def __getattribute__(self,item):
		print('__getattribute__')
		return super().__getattribute__(item)

	def __getattr__(self,item):
		print('__getattr__')
	
	def __setattr__(self,key,value):
		print('__setattr__')
		super().__setattr__(key,value)
	
	def __delattr__(self,item):
		print('__delattr__')
		super().__delattr__(item)

c = C()
c.x
# __getattribute__
# __getattr__

c.x = 1
# __setattr__

del c.x
# __delattr__	

** 采用object(所有类的基类)调用方法,super()也是用来调用方法**

8.1.7 描述符

描述符就是将某种特殊类型的类的实例指派给另一个类的属性。
get(self, instance, owner)用于访问属性,它返回属性的值。
set(self, instance, value)将在属性分配操作中调用,不返回任何内容。
del(self, instance)控制删除操作,不返回任何内容。
【例子】

class  MyDecriptor:
	def  __get__(self,instance,owner):
		print('__get__',instance,owner)
	
	def __set__(self,instance,value):
		print('__set__',self,instance,value)
	
	def __delete__(self,instance):
		print('__delete__',self,instance)

class Test:
	x = MyDecriptor():

t = Test()
t.x
# __get__<__main__.MyDecriptor object at 0x000000CEAAEB6B00> <__main__.Test object at 0x000000CEABDC0898> 
t.x = 'x-man'
# __set__ <__main__.MyDecriptor object at 0x00000023687C6B00> <__main__.Test object at 0x00000023696B0940> x-man

del t.x
# __delete__ <__main__.MyDecriptor object at 0x000000EC9B160A90> <__main__.Test object at 0x000000EC9B160B38>

8.1.8 定制序列

协议(Protocols)与其他编程语言中的接口很相似,它规定你哪些方法必须要定义。然而,在python中的协议就显得那么不正式。在Python中,协议更像是一种指南。

  1. 容器类型的协议
  • 如果希望定制的容器是不可变的,只需要定义__len__()和__gettiem__()方法。
  • 如果希望定制的容器是可变的话,除了__len__和__getitem__方法,还需要定义__setitem__和__delitem__两个方法。
    【例子】编写一个不可改变的自定义列表,要求记录列表中每个元素被访问的次数
class CountList:
	def __init__(self,*args):
		self.values = [x for in args]
		self.count = {}.fromkeys(range(len(self.values)),0)
	
	def __len__(self):
		return len(self.values)
	
	def __getitem__():
		self.count[item] +=1
		return self.values[item]

c1 = CountList(1,3,5,7,9)
c2 = CountList(2,4,6,8,10)

print(c1[1])	# 3
print(c2[2])	# 6
print(c1[1]+c2[1]) 	# 7

print(c1.count)		# {0:0,1:2,2:0,3:0,4:0}
print(c2.count)		# {0:0,1:1,2:1,3:0,4:0}

len(self)定义当被len()调用时的行为(返回容器中元素的个数)
getitem(self,key)定义获取容器中元素的行为,相当于self[key]
setitem(self, key, value)定义设置容器中指定元素的行为,相当于self[key] = value。
delitem(self, key)定义删除容器中指定元素的行为,相当于del self[key]。
【例子】编写一个可改变的自定义列表,要求记录列表中每个元素被访问的次数

class CountList:
	def __init__(self,*args):
		self.values = [x for i in args]
		self.count = {}.fromkeys(range(len(self.values)),0)

	def __len__(self):
		return len(self.values)

	def __getitem__(self,item):
		self.count[item] += 1
		return self.values[item]
	
	def __setitem__(self,key,value):
		self.values[key] = value
	def __delitem__(self,key):
		del self.values[key]
		for i in range(0,len(self.values)):
			if i >=key:
				self.count[i] = self.count[i+1]
		self.count.pop(len(self.values))

c1 = CountList(1,3,5,7,9)
c2 = CountList(2,4,6,8,10)
print(c1[1])	# 3
print(c2[2]) 	# 6

c1 = CountList(1, 3, 5, 7, 9)
c2 = CountList(2, 4, 6, 8, 10)
print(c1[1])  # 3
print(c2[2])  # 6
c2[2] = 12
print(c1[1] + c2[2])  # 15
print(c1.count)
# {0: 0, 1: 2, 2: 0, 3: 0, 4: 0}
print(c2.count)
# {0: 0, 1: 0, 2: 2, 3: 0, 4: 0}
del c1[1]
print(c1.count)
# {0: 0, 1: 0, 2: 0, 3: 0}

8.1.9 迭代器

迭代器是Python中最强大的功能之一,是访问集合元素的一种方式
迭代器是一个可以记住遍历的位置的对象
迭代器对象从集合的第一个元素开始访问,知道所有的元素被访问结束
迭代器只能往前不会后退
字符串,列表,字典或元祖对象都可用于作为创建迭代器

string = 'lsgogroup'
for c in string:
    print(c)

'''
l
s
g
o
g
r
o
u
p
'''

for c in iter(string):
    print(c)

【例子】

links = {'B':'baidu,'A':'alibaba','T':'Tencent'}
for each in link:
	print('%s ->%s' %(each,links[each]))


# B -> 百度
# A -> 阿里
# T -> 腾讯
for each in iter(links):
    print('%s -> %s' % (each, links[each]))
	
  • 迭代器有两个基本的方法:iter()和next():
    1.iter(object)函数用来生成迭代器
    1. next(iterator,[default])返回迭代器的下一个项目: iterator–可迭代对象,default–可选,用于设置在没有下一个元素时返回默认值,如果不设置,又没有下一个元素则会触发StopIteration异常。
      【例子】
links = {'B':'baidu','A':'Alibaba','T':'Tencent'}
it = iter(links)
print(next(it))	# B
print(next(it))	# A
print(next(it))	# T
print(next(it))	# StopIteration

it = iter(links)
while true:
	try:
		each = next(it)
	except StopIteration:
		break
	print(each)

# B 
# A
# T

把一个类作为一个迭代器使用需要在类中实现两个魔方方法__ietr__()与__next__()
iter(self)定义当迭代容器中的元素的行为,返回一个特殊的迭代器对象, 这个迭代器对象实现了 next() 方法并通过 StopIteration 异常标识迭代的完成。
next() 返回下一个迭代器对象。
StopIteration 异常用于标识迭代的完成,防止出现无限循环的情况,在 next() 方法中我们可以设置在完成指定循环次数后触发 StopIteration 异常来结束迭代。
【例子】

class Fibs:
	def __init__(self,n=10):
		self.a = 0
		self.b = 1
		self.n = n
	def __iter__(self):
		return self
	def __next__(self):
		self.a,self.b = self.b, self.a+self.b
		if self.a > self.n:
			raise StopIteration
		return self.a
fibs = Fibs(100)
for each in fibs:
	print(each,end = ' ')
# 1 1 2 3 8 13 21 34 55 89 

8.1.10 生成器

在Python中,使用了yield的函数被称为生成器(generator)。
跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。
在调用生成器运行的过程中,每次遇到yield时函数会暂停并保存当前所有的运行信息,返回yield的值,并在下一次执行next()方法时从当前位置继续运行。
调用一个生成器函数,返回的是一个迭代器对象
【例子】

def myGen():
	print('生成器执行!')
	yield 1
	yield 2

myG = myGen()
print(next(myG))
# 生成器执行!
# 1

print(next(myG))	
# 2 
print(next(myG))
# StopIteration

myG = myGen()
for each in myG:
	print(each)
# 生成器执行
# 1
# 2

【例子】

def libs(n):
	a = 0
	b = 1
	while True:
		a,b = b,a+b
	if a>n:
		return
	yield a

for each in libs(100):
	print(each,end = ' ')
# 1 1 2 3 5 8 13 21 34 55 89

8.1.11 【练习题】

1、上面提到了许多魔法方法,如__new__,init, str,rstr,getitem,__setitem__等等,请总结它们各自的使用方法。
2、利用python做一个简单的定时器类
要求:
定制一个计时器的类。
start和stop方法代表启动计时和停止计时。
假设计时器对象t1,print(t1)和直接调用t1均显示结果。
当计时器未启动或已经停止计时时,调用stop方法会给予温馨的提示。
两个计时器对象可以进行相加:t1+t2。
只能使用提供的有限资源完成。

8.2 模块

模块是一个包含所有你定义的函数和变量的文件,其后缀名是.py。模块可以被别的程序引入,以使用该模块中的函数等功能。这也是使用 Python 标准库的方法。

  • 解释器和编译器的区别?
    定义上:
    1. 编译器:把编程代码转换成机器码指令(不同CPU的机器指令不同),往往是在「执行」之前完成,产出是一种可执行的文件,产物是「另一份代码」。
    2. 解释器:直接执行由编程语言或脚本语言编写的代码,并不会把源代码预编译成机器码。把程序源代码一行一行的读懂然后执行,发生在运行时,产物是「运行结果」。
      主要观点仍然是:解释器立即执行代码,编译器为稍后的执行准备好源代码。所有实际的差异都因为他们有不同的目标。

8.2.1 什么是模块?

容器–>数据的封装
函数–>语句的封装
类—>属性和方法的封装
模块–>程序文件
【例子】创建一个hello.py文件

hello.py
def hi():
	print('Hi,everyone,I Love Isgogroup')

8.2.2 命名空间-NameSpace

命名空间因为对象的不同,也有所区别,分为如下几种:
内置命名空间(Built-in NameSpace):Python运行起来,他们就存在了。内置函数的命名空间都属于内置命名空间,所以我们可以在任何程序中直接运行它们,比如Id()不需要做什么操作,拿过来就直接使用了。
全局命名空间(modual :Global NameSpace):每个创建它自己所拥有的全局命名空间,不同模块的全局命名空间彼此相互独立,不同模块中相同名称的命名空间,也会因为模块的不同而不相互干扰。
本地命名空间(Function & Class: Local NameSpaces):模块中有函数或者类,每个函数或者类所定义的命名空间就是本地命名空间。如果函数返回了结果或者抛出异常,则本地命名空间也就结束了。
程序在查询上述三种命名空间的时候,就按照从里到位的顺序,即:Local NameSpaces–>Global NamesSpaces—>Built-in-NameSpaces。
【例子】

import hello

hello.hi()	# Hi everyone,I love Lsgogroup
hi()			# NameError: name 'hi' is not defined

8.2.3 导入模块

【例子】创建一个模块 TemperatureConversion.py

# TempratureConversion.py
def c2f(cel):
	fah = cel*1.8 +32
	return fah

def f2c(fah):
	cel = (fah-32)/ 1.8
  • 第一种:import 模块名
    【例子】
import TempratureConversion

print('32摄氏度 = %.2f华氏度' % TempratureConversion.c2f(32))
print('99华氏度 = %.2f摄氏度' % TempratureConversion.f2c(99))

# 32摄氏度 = 89.6华氏度
# 00华氏度 = 37.22摄氏度
  • 第二种:from 模块名 import 函数名。但是这种就是需要担心函数名重复的问题

【例子】

from TemperatureConversion import c2f, f2c

print('32摄氏度 = %.2f华氏度' % c2f(32))
print('99华氏度 = %.2f摄氏度' % f2c(99))

# 32摄氏度 = 89.60华氏度
# 99华氏度 = 37.22摄氏度
  • 第三种:import 模块名 as 新名字
    【例子】
import TempratureConversion as tc
print('32摄氏度 = %.2f华氏度' % tc.c2f(32))
print('99华氏度 = %.2f摄氏度' % tc.f2c(99))

8.2.4 if name == ‘main

  1. 程序入口
    对于编程语言来说,程序必须要有一个执行入口,而Python不同,属于脚本语言。不像编译型语言那样先将程序编译成二进制再运行,而是动态的逐行解释运行。也就是从脚本第一行开始运行,没有统一的入口。
    假设有一个const.py文件,内容如下:
# const.py文件
PI = 3.14

def main():
	print("PI: ",PI)

main()
# PI: 3.14

现在,我们写一个用于计算圆面积的area.py文件,area.py文件需要用到const.py文件中的PI变量。从const.py中,我们把PI变量导入area.py:

from const import PI

def calc_round_area(radius):
	return PI*(radius **2)
def main():
	print('round area',calc_round_area(2))

main()

PI:3.14
round area: 12.56

我们看到const.py中的main函数也被运行了,实际上不希望它被执行,因为const.py提供的main函数只是为了测试常量定义。这时 if __name__ ==' __main__'派上了用场,我们把const.py改一下,添加 if __name__ =='__main__':
```py
# 修改const.py文件
PI = 3.14

def main():
    print("PI:", PI)

if __name__ == "__main__":
    main()

运行 const.py,输出如下:
PI: 3.14
运行 area.py,输出如下:
round area: 12.56
name:是内置变量,可用于表示当前模块的名字

import const

print(__name__)  
# __main__
print(const.__name__)
# const
  • name == ‘main’:假如你叫小明.py,在朋友眼中,你是小明(name == ‘小明’);在你自己眼中,你是你自己(name == ‘main’)。
  • 如果一个 .py 文件(模块)被直接运行时,其__name__值为__main__,即模块名为__main__(在自己眼中)。
  • if name =='main’的意思是:当.py文件被直接运行时,if name == ‘main’ 之下的代码块将被运行;当.py文件以模块形式被导入时,if name == 'main’之下的的代码块不被运行。
  • 目的是为了选择程序入口;由于模块之间相互引用,不同模块可能有这样的定义,而程序入口只有一个。到底哪个程序入口被选中,这取决于__name__的值。
  1. __name__反映一个包的结构
    __name__是内置变量,可用于反映一个包的结构。假设有一个包,包的结构如下:
a
├── b
│     ├── c.py
│   	 └── __init__.py
└── __init__.py

在Package(包)中,文件c.py,init.py,init.py的内容都为:
print(name)
当一个.py文件(模块)被其他.py文件(模块)导入时,我们在命令行执行:

Python -c "import a.b.c"
# a
# a.b
# a.b.c

由此可见:name__可以清晰地反映一个模块在包中的层次。
3. main.py文件与Python -m
Python的-m参数用于将一个模块或者包作为一个脚本执行,而__main
.py文件相当于是一个包"入口程序"。
运行Python程序的两种方式:
python xxx.py,直接运行xxx.py文件
python -m xxx.py,把xxx.py当做模块运行
4. mian.py的作用
一个包的结构:
package
├── init.py
└── main.py
其中文件__init__.py的内容

import sys

print("__init__")
print(sys.path)

其中文件__main__.py的内容:

import sys

print("__main__")
print(sys.path)

接下来,我们运行这个package,使用python -m package运行,输出结果:

__init__
['', ...]

__main__
['', ...]

使用python package运行,输出结果:

__main__
['package', ...]

【小结】
当加上-m参数时,Python会把当前工作目录添加到sys.path中;而不加-m时,Python则会把脚本所在目录添加到sys.path中。
当加上-m参数时,Python会先将模块或者包导入,然后再执行。
main.py文件是一个包或者目录的入口程序。不管是用python package还是用python -m package运行,main.py文件总是被执行。
【参考来源】:https://blog.csdn.net/yjk13703623757/article/details/77918633

8.2.4 搜索路径

当解释器遇到import语句,如果模块在当前的搜索路径就会被导入。
【例子】

import sys 
print (sys.path)

我们使用 import 语句的时候,Python 解释器是怎样找到对应的文件的呢?
这就涉及到 Python 的搜索路径,搜索路径是由一系列目录名组成的,Python 解释器就依次从这些目录中去寻找所引入的模块。
这看起来很像环境变量,事实上,也可以通过定义环境变量的方式来确定搜索路径。
搜索路径是在 Python 编译或安装的时候确定的,安装新的库应该也会修改。搜索路径被存储在 sys 模块中的 path 变量中。

8.2.6 Package(包)

包是一种管理Python模块命名空间的形式,采用“点模块名称”。
创建包分为三个步骤:
创建一个文件夹,用于存放相关的模块,文件夹的名字即包的名字。
在文件夹中创建一个 init.py 的模块文件,内容可以为空。
将相关的模块放入文件夹中。
【包的创建模板】

sound/                          顶层包
      __init__.py               初始化 sound 包
      formats/                  文件格式转换子包
              __init__.py
              wavread.py
              wavwrite.py
              aiffread.py
              aiffwrite.py
              auread.py
              auwrite.py
              ...
      effects/                  声音效果子包
              __init__.py
              echo.py
              surround.py
              reverse.py
              ...
      filters/                  filters 子包
              __init__.py
              equalizer.py
              vocoder.py
              karaoke.py
              ...

在导入一个包的时候,Python会根据sys.path中的目录来寻找这个包中包含的子目录。
【访问包中模块】

  1. 访问一:目录只有包含一个叫做 init.py 的文件才会被认作是一个包,最简单的情况,放一个空的 init.py 就可以了。
    import sound.effects.echo
    这将会导入子模块 sound.effects.echo。 他必须使用全名去访问:
    sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)

  2. 访问二:导入子模块:from sound.effects import echo,是直接导入本文件中,会有担心函数名重复
    这同样会导入子模块: echo,但是不再不需要那些冗长的前缀,所以可以这样使用:
    echo.echofilter(input, output, delay=0.7, atten=4)

  3. 访问三:直接导入一个函数或者变量:from sound.effects.echo import echofilter
    同样的,这种方法会导入子模块: echo,并且可以直接使用他的 echofilter() 函数:
    echofilter(input, output, delay=0.7, atten=4)
    【注意】
    注意当使用 from package import item 这种形式的时候,对应的 item 既可以是包里面的子模块(子包),或者包里面定义的其他名称,比如函数,类或者变量。
    设想一下,如果我们使用 from sound.effects import * 会发生什么?
    Python 会进入文件系统,找到这个包里面所有的子模块,一个一个的把它们都导入进来。
    导入语句遵循如下规则:如果包定义文件 init.py 存在一个叫做 all 的列表变量,那么在使用 from package import * 的时候就把这个列表中的所有名字作为包内容导入。
    这里有一个例子,在 sounds/effects/init.py中包含如下代码:
    all = [“echo”, “surround”, “reverse”]
    这表示当你使用 from sound.effects import *这种用法时,你只会导入包里面这三个子模块。
    如果 all 真的没有定义,那么使用from sound.effects import *这种语法的时候,就不会导入包 sound.effects 里的任何子模块。他只是把包 sound.effects 和它里面定义的所有内容导入进来(可能运行__init__.py里定义的初始化代码)。
    这会把 init.py 里面定义的所有名字导入进来。并且他不会破坏掉我们在这句话之前导入的所有明确指定的模块。

import sound.effects.echo
import sound.effects.surround
from sound.effects import *

这个例子中,在执行 from…import 前,包 sound.effects 中的 echo 和 surround 模块都被导入到当前的命名空间中了。

通常我们并不主张使用 * 这种方法来导入模块,因为这种方法经常会导致代码的可读性降低。

8.2.7 【练习题】

1、怎么查出通过 from xx import xx导⼊的可以直接调⽤的⽅法?

2、了解Collection模块,编写程序以查询给定列表中最常见的元素。

题目说明:

输入:language = [‘PHP’, ‘PHP’, ‘Python’, ‘PHP’, ‘Python’, ‘JS’, ‘Python’, ‘Python’,‘PHP’, ‘Python’]

输出:Python

“”"
Input file
language = [‘PHP’, ‘PHP’, ‘Python’, ‘PHP’, ‘Python’, ‘JS’, ‘Python’, ‘Python’,‘PHP’, ‘Python’]

Output file
Python
“”"
def most_element(language):
“”" Return a list of lines after inserting a word in a specific line. “”"

# your code here

你可能感兴趣的:(编程语言,python)