<file_sep>import requests
import bs4
import re
import openpyxl

def open_url(url):
    headers = {"user-agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36"}
    res = requests.get(url, headers=headers)
    return res

def find_data(res):
    data = []
    soup = bs4.BeautifulSoup(res.text, "html.parser")
    content = soup.find(id="mp-editor")
    targets = content.find_all("p")
    targets = iter(targets)
    for each in targets:
        if each.text.isnumeric():
            data.append([
               re.search(r'\[(.+)\]',  next(targets).text).group(1),
                re.search(r'\d.*',  next(targets).text).group(),
                re.search(r'\d.*',  next(targets).text).group(),
                re.search(r'\d.*',  next(targets).text).group()])
    return data

def to_excel(data):
    wb = openpyxl.Workbook()
    wb.guess_types = True
    ws = wb.active
    ws.append(["城市", "平均房价", "平均工资", "房价工资表"])
    for each in data:
        ws.append(each)
    wb.save("2017年中国主要城市房价工资比排行榜.xlsx")
    
def main():
    url = "http://www.sohu.com/a/154203859_99913782"
    res = open_url(url)
    data = find_data(res)
    to_excel(data)
if __name__ == "__main__":
    main()
<file_sep>import socket

# 1 买手机
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 2 拨号
phone.connect(('127.0.0.1', 9094))

# 3 发、收消息
while True:
	msg = input('>> ').strip() # phone.send(b'')
	phone.send(msg.encode('utf-8'))
	if not msg:
		continue
	data = phone.recv(1024)
	print(data.decode('utf-8'))

# 4 关闭
phone.close()
<file_sep># 面向对象进阶

# @property装饰器
"""
python中的属性和方法访问的权限的问题。
虽然我们不建议将属性设置为私有，但是如果直接将属暴露给外界也是有问题的，比如我们没有办法
检查赋给属性的值是否有效。我们之前的建议是将属性命名以下划线开头，通过这种方式暗示属性
是受保护的，不建议直接访问，那么如果想访问属性，可以通过属性的getter（访问器）和setter
(修改器)方法进行操作。如果要做到这点，就可以考虑使用@property包装器来包装getter和
setter方法,使得对属性的访问既安全又方便
"""

'''
class Person(object):
	def __init__(self,name,age):
		self._name = name
		self._age = age
	# 访问器 - getter方法
	#@property
	def name(self):
		return self._name
	# 访问器 - getter方法
	#@property
	def age(self):
		return self._age
	# 修改器 - setter方法
	#@age.setter
	def age(self,age):
		self._age = age
	def play(self):
		if self._age <= 16:
			print('%s正在玩飞行棋.' % self._name)
		else:
			print('%s正在玩斗地主.' % self._name)

def main():
	person = Person('王大锤',12)
	person.play()
	person.age = 22
	person.play()
	person.name = '百元房' # AttributeError: can't set attribute
	print(person.name) # 上面的语句是可以改掉named的
main()
'''

# __slots__魔法
"""
pthon是一门动态语言,动态语言允许我们在程序运行是给对象绑定新的属性或方法,当然也可以对
已经绑定的属性和方法进行解绑定。但是如果我们需要限定自定义类型的对象只能绑定某些属性
，可以通过在类中定义__slots__变量来进行限定。
需要注意的是__slots__的限定只对当前类的对象生效，对子类不起任何作用
"""

'''
class Person(object):
	#限定Person对象只能绑定_name,_age和_gender属性
	__slots__ = ('_name','_age','_gender')
	def __init__(self,name,age):
		self._name = name
		self._age = age
	@property
	def name(self):
		return self._name
	@property
	def age(self):
		return self._age
	@age.setter
	def age(self,age):
		self._age = age
	def play(self):
		if self._age <= 16:
			print('%s正在玩飞行棋.' % self._name)
		else:
			print('%s正在玩斗地主.' % self._name)
def main():
	person = Person('王大锤',22)
	person.play()
	person._gender = '男'
	# preson._is_gay = True # NameError: name 'preson' is not defined
main()
'''

# 静态方法和类方法
'''
之前，我们在类中定义的方法都是对象方法，也就是说这些方法都是发送给对象的消息。
实际上，我们写在类中的方法并不需要都是对象方法，例如我们定义一个“三角形”类，
通过传入三条边长来构造三角形，并提供计算周长和面积的方法，但是传入的三条边长未必能构造出
三角形对象，因此我们可以先写一个方法来验证三条边长是否可以构成三角形，这个方法很显然就不是
对象方法，因为在调用这个方法时三角形对象尚未创建出来（因为都不知道三条边能不能构成三角形），
所以这个方法是属于三角形类而并不属于三角形对象的。我们可以使用静态方法来解决这类问题
'''
'''
from math import sqrt
class Triangle(object):
	def __init__(self,a,b,c):
		self._a = a
		self._b = b
		self._c = c
	@staticmethod
	def is_valid(a,b,c):
		return a+b>c and b+c>a and a+c>b
	def perimeter(self):
		return self._a + self._b + self._c
	def area(self):
		half = self.perimeter()/2
		return sqrt(half*(half-self._a)*(half-self._b)*(half-self._c))
def main():
	a,b,c = 3,4,5
	# 静态方法和类方法都是通过给类发消息来调用的
	if Triangle.is_valid(a,b,c):
		t = Triangle(a,b,c)
		print(t.perimeter())
		# 也可以通过给类发消息来调用对象方法但是要传入接受消息的对象作为参数
		# print(Triangle.perimeter(t))
		print(t.area())
main()
'''
'''
from time import time,localtime,sleep
class Clock(object):
	"""数字时钟"""
	def __init__(self,hour=0,minute=0,second=0):
		self._hour = hour
		self._minute = minute
		self._second = second
	@classmethod
	def now(cls):
		ctime = localtime(time())
		return cls(ctime.tm_hour,ctime.tm_min,ctime.tm_sec)
	def run(self):
		self._second+=1
		if self._second==60:
			self._second=0
			self.minute+=1
			if self._minute==60:
				self._minute=0
				self._hour+=1
				if self._hour==24:
					self._hour=0
	def show(self):
		return '%02d:%02d:%02d'%(self._hour,self._minute,self._second)
def main():
	# 通过类方法创建对象病获取系统弄时间
	clock = Clock.now()
	while True:
		print(clock.show())
		sleep(1)
		clock.run()
main()
'''

# 类之间的关系
"""
简单来说，类和类之间的关系有三种：is-a,has-a和use-a关系
	is-a关系也叫继承或泛化，比如学生和人的关系、手机和电子产品的关系都属于继承关系
	has-a关系通常称之为关联，比如部门和员工的关系，汽车和引擎的关系都属于关联关系；
	关联关系如果是整体和部分的关联，称之为聚合关系；如果整体进一步负责了部分的生命周期
	(整体和部分不可分割，同时同在同事消亡)，那么就是最强的关联关系，我们称之为合成关系
	use-a关系称之为依赖，比如司机有一个驾驶的行为(方法),其中(的参数)使用到了汽车，那么
	司机和汽车的关系就是依赖关系
	
	我们可以使用一种叫做UML（统一建模语言）的东西来进行面向对象建模，
	其中一项重要的工作就是把类和类之间的关系用标准化的图形符号描述出来。
	
	利用类之间的这些关系，我们可以在已有类的基础上来完成某些操作，也可以在已有类的基础上
	创建新的类，这些都是实现代码复用的重要手段。复用现有的代码不仅可以减少开发的工作量，
	也有利于代码的管理和维护，这是我们在日常工作中都会使用到的技术手段。
"""

# 继承和多态
"""
在已有类的基础上创建新类，其中一种方法就是让一个类从另一个类那里将属性和方法继承下来，从而
减少重复代码的编写。提供继承信息的称之为父类，也叫超类或基类；得到继承信息的我么称之为子类
，也叫派生类或衍生类。子类除了继承父类提供的属性和方法，还可以自定义自己特有的属性和方法，
所以子类比父类拥有更多的能力。在实际开发中，我们经常会用子类对象去替换掉一个父类对象，
这是面向对象编程中一个常见的行为。
"""
'''
class Person(object):
	def __init__(self,name,age):
		self._name = name
		self._age = age
	@property
	def name(self):
		return self._name
	@property
	def age(self):
		return self._age
	@age.setter
	def age(self,age):
		self._age = age
	def play(self):
		print('%s正在愉快的玩耍.' % self._name)
	def watch_av(self):
		if self._age >= 18:
			print('%s正在观看爱情动作片.'%self._name)
		else:
			print('%s正在观看《熊出没》.'%self._name)
class Student(Person):
	def __init__(self,name,age,grade):
		super().__init__(name,age)
		self._grade = grade
	@property
	def grade(self):
		return self._grade
	@grade.setter
	def grade(self,grade):
		self._grade = grade
	def study(self,course):
		print('%s的%s正在学习%s.'%(self._grade,self._name,course))
class Teacher(Person):
	def __init__(self,name,age,title):
		super().__init__(name,age)
		self._title = title
	@property
	def title(self):
		return self.title
	@title.setter
	def title(self,title):
		self._title = title
	def teach(self,course):
		print('%s%s正在讲%s'%(self._name,self._title,course))
def main():
	stu = Student('王大锤',15,'初三')
	stu.study('数学')
	stu.watch_av()
	t = Teacher('王朝阳',38,'老教授')
	t.teach('python程序设计')
	t.watch_av()
main()
'''
"""
子类在继承了父类的方法后，可以对父类已有的方法给出新的实现版本，这个动作称之为方法重写(override)
.通过方法重写我们可以让父类的同一个行为在子类中拥有不同的实现版本，当我们调用这个经过子类
重写方法时，不同的子类对象会表现出不同的行为，这个就是多态(morphism)
"""
'''
在下面的代码中，我么将Pet类处理成了一个抽象类，所谓抽象类就是不能够创建对象的类，这种类
的存在就是专门为了让其他类去继承它 python 从语法层面并没有型java或c#那样提供对抽象类
的支持，但是我么可以通过 abc 模块的 ABCMeta 元类 和 abstractmethod 包装器来达到
抽象类的效果，如果一个类中存放抽象方法那么这个类就不能实例化(创建对象)

上面的代码中，Dog和Cat两个子类分别对Pet类中的make_voice抽象方法进行了重写并给出了
不同的实现版本，当我们在main函数中调用该方法时，这个方法就表现出了多态行为
（同样的方法做了不同的事情）。
from abc import ABCMeta,abstractmethod
class Pet(object,metaclass=ABCMeta):
	"""宠物"""
	def __init__(self,nickname):
		self._nickname = nickname
	@abstractmethod
	def make_voice(self):
		"""发出声音"""
		pass
class Dog(Pet):
	"""狗"""
	def make_voice(self):
		print('%s:汪汪汪...'%self._nickname)
class Cat(Pet):
	"""猫"""
	def make_voice(self):
		print('%s:喵...喵...'%self._nickname)
def main():
	pets = [Dog('旺财'),Cat('凯蒂'),Dog('大黄')]
	for pet in pets:
		pet.make_voice()
main()
'''

# 综合案例
# 案例1：奥特曼打小怪兽
'''
from abc import ABCMeta,abstractmethod
from random import randint,randrange

class Fighter(object,metaclass=ABCMeta):
	"""战斗者"""
	# 通过__slots__魔法限定对象可以绑定的成员变量
	__slots__=('_name','_hp')
	def __init__(self,name,hp):
		"""初始化方法
		:param name:名字
		:param hp 生命值
		"""
		self._name = name
		self._hp = hp
	@property
	def name(self):
		return self._name
	@property
	def hp(self):
		return self._hp
	@hp.setter
	def hp(self,hp):
		self._hp = hp if hp >= 0 else 0
	@property
	def alive(self):
		return self._hp > 0
	@abstractmethod
	def attack(self,other):
		"""攻击
		:param other: 被攻击的对象
		"""
		pass
class Ultraman(Fighter):
	"""奥特曼"""
	__slote__=('_name','_hp','_mp')
	def __init__(self,name,hp,mp):
		"""初始化方法
		:param name:名字
		:param hp:生命值
		:param mp:魔法值
		"""
		super().__init__(name,hp)
		self._mp = mp
	def attack(self,other):
		other.hp -= randint(15,25)
	def huge_accack(self,other):
		"""九级必杀技(打掉对方至少50点或四分之三血)
		:param other: 被攻击的对象
		:return: 使用成功返回True否则返回False
		"""
		if self._mp >= 50:
			self._mp -= 50
			injury = other.hp*3//4
			injury = injury if injury >= 50 else 50
			other.hp -= injury
			return True
		else:
			self.attack(other)
			return False
	def magic_attack(self,others):
		"""魔法攻击
		:param others:被攻击的群体
		:return :使用魔法成功返回True否则返回False
		"""
		if self._mp >= 20:
			self._mp -= 20
			for temp in others:
				if temp.alive:
					temp.hp -= randint(10,15)
			return True
		else:
			return False
	def resume(self):
		"""恢复魔法值"""
		incr_point = randint(1,10)
		self._mp += incr_point
		return incr_point
	def __str__(self):
		return '~~~%s奥特曼~~~\n'%self._name + \
		'生命值：%d\n' % self._hp + \
		'魔法值：%d\n' % self._mp
class Monster(Fighter):
	"""小怪兽"""
	__slots__=('_name','_hp')
	def attack(self,other):
		other.hp -= randint(10,20)
	def __str__(self):
		return '~~~%s小怪兽~~~->' % self._name + '生命值：%d   ' % self._hp 
def is_any_alive(monsters):
	"""判断有没有小怪兽是活着的"""
	for monster in monsters:
		if monster.alive > 0:
			return True
	return False
def select_alive_one(monsters):
	"""选中一只活着的怪兽"""
	monsters_len = len(monsters)
	while True:
		index = randrange(monsters_len)
		monster = monsters[index]
		if monster.alive > 0:
			return monster
def display_info(ultraman,monsters):
	"""显示奥特曼和小怪兽的信息"""
	print(ultraman)
	for monster in monsters:
		print(monster,end='\t')
def main():
	u = Ultraman('小天使',1000,120)
	m1 = Monster('狄仁杰',250)
	m2 = Monster('白元芳',500)
	m3 = Monster('王大锤',750)
	ms = [m1,m2,m3]
	fight_round = 1
	while u.alive and is_any_alive(ms):
		print('=======第%d回合=======' % fight_round)
		m = select_alive_one(ms) # 选中一只小怪兽
		skill = randint(1,10) # 通过随机数选择使用哪种技能
		if skill <= 1: # 60%的概率使用普通攻击
			print('%s使用普通攻击打了%s.' % (u.name,m.name))
			u.attack(m)
			print('%s的魔法值恢复了%d点.' % (u.name,u.resume()))
		elif skill <= 2: # 30%的概率使用魔法攻击(可能因为魔法值不足攻击失败)
			if u.magic_attack(ms):
				print('%s使用了魔法攻击.' % u.name)
			else:
				print('%s使用魔法失败.' % u.name)
		else: # 10%的概率使用究极必杀技(如果魔法值不足则使用普通攻击)
			if u.huge_accack(m):
				print('%s使用了究极必杀技虐了%s.' % (u.name,m.name))
			else:
				print('%s使用普通攻击击打了%s.' % (u.name,m.name))
				print('%s的魔法值恢复了%d点.' % (u.name,u.resume()))
		if m.alive > 0: # 如果选中的小怪兽没有死就回击奥特曼
			print('%s回击了%s.' % (m.name,u.name))
			m.attack(u)
		display_info(u,ms) # 每个回合结束后显示奥特曼和小怪兽的信息
		fight_round += 1
	print('\n=======战斗结束!=======\n')
	if u.alive > 0:
		print('%s奥特曼胜利!' % u.name)
	else:
		print('小怪兽胜利!')
main()
'''

# 案例2：扑克游戏
'''
import random
class Card(object):
	"""一张牌"""
	def __init__(self,suite,face):
		self._suite = suite
		self._face = face
	@property
	def face(self):
		return self._face
	@property
	def suite(self):
		return self._suite
	def __str__(self):
		if self._face == 1:
			face_str = 'A'
		elif self._face == 11:
			face_str = 'J'
		elif self._face == 12:
			face_str = 'Q'
		elif self._face == 13:
			face_str = 'K'
		else:
			face_str = str(self._face)
		return '%s%s' % (self._suite,face_str)
	def __repr__(self):
		return self.__str__()
class Poker(object):
	"""一副牌"""
	def __init__(self):
		self._cards = [Card(suite,face) for suite in '♠♥♣♦' for face in
		range(1,14)]
		self._current = 0
	@property
	def cards(self):
		return self._cards
	def shuffle(self):
		"""洗牌(随机乱序)"""
		self._current = 0
		random.shuffle(self._cards)
	@property
	def next(self):
		"""发牌"""
		card = self._cards[self._current]
		self._current += 1
		return card
	@property
	def has_next(self):
		"""还有没有牌"""
		return self._current < len(self._cards)
class Player(object):
	"""玩家"""
	def __init__(self,name):
		self._name = name
		self._cards_on_hand = []
	@property
	def name(self):
		return self._name
	@property
	def cards_on_hand(self):
		return self._cards_on_hand
	def get(self,card):
		"""摸牌"""
		self._cards_on_hand.append(card)
	def arrange(self,card_key):
		"""玩家整理手上的牌"""
		self._cards_on_hand.sort(key=card_key)
# 排序规则-先根据花色在根据点数排序
def get_key(card): # 这里可以通过类外的函数直接获取到类中的东西
	return (card.suite,card.face)

def main():
	p = Poker()
	p.shuffle()
	players = [Player('东邪'),Player('西毒'),Player('南帝'),Player('北丐')]
	for _ in range(13):
		for player in players:
			player.get(p.next)
	for player in players:
		print(player.name + ':',end=' ')
		player.arrange(get_key)
		print(player.cards_on_hand)
main()
"""这里可以在上面的代码基础上写一个简单的扑克游戏，例如21点"""
'''

# 工资结算系统
"""
某公司有三种类型员工，分别是部门经理，程序员，销售员
需要设计一个工资结算系统。 根据童工的员工信息来计算月薪
部门经理的月薪是每月固定1500元
程序员的月薪是按本月工作时间计算，每小时150元
销售员的月薪是1200元的底薪加上销售额5%的提成
"""
'''
from abc import ABCMeta,abstractmethod

class Employee(object,metaclass=ABCMeta):
	"""员工"""
	def __init__(self,name):
		"""初始化方法
		:param name:姓名
		"""
		self._name = name
	@property
	def name(self):
		return self._name
	@abstractmethod
	def get_salary(self):
		"""
		获得月薪
		:rerurn : 底薪
		"""
		pass
class Manager(Employee):
	"""部门经理"""
	def get_salary(self):
		return 15000.0
class Programmer(Employee):
	"""程序员"""
	def __init__(self,name,working_hour=0):
		super().__init__(name)
		self._working_hour = working_hour
	@property
	def working_hour(self):
		return self._working_hour
	@working_hour.setter
	def working_hour(self,working_hour):
		self._working_hour = working_hour if working_hour > 0 else 0
	def get_salary(self):
		return 150.0*self._working_hour
class Salesman(Employee):
	"""销售员"""
	def __init__(self,name,sales=0):
		super().__init__(name)
		self._sales = sales
	@property
	def sales(self):
		return self._sales
	@sales.setter
	def sales(self,sales):
		self._sales = sales if sales > 0 else 0
	def get_salary(self):
		return 1200.0 + self._sales * 0.05
def main():
	emps = [Manager('刘备'),Programmer('诸葛亮'),Manager('曹操'),Salesman('荀或'),
	Salesman('吕布'),Programmer('张辽'),Programmer('赵云')]
	for emp in emps:
		if isinstance(emp,Programmer):
			emp.working_hour = int(input('请输入%s本月的工作时间:' % emp.name))
		elif isinstance(emp,Salesman):
			emp.sales = float(input('请输入%s本月的销售额:' % emp.name))
		#同样是接收get_salary这个消息但是不同的员工表现出了不同的行为
		print('%s本月工资为:￥%s元' % (emp.name,emp.get_salary()))
main()
'''
<file_sep># 网络编程入门和网络应用开发

"""
计算机网络基础
<NAME>老师的经典之作《计算机网络》或Kurose和Ross老师合著的
《计算机网络:自顶向下方法》来了解计算机网络的相关知识。

计算机网络发展史
	1960s - 美国国防部ARPANET项目问世，奠定了分组交换网络的基础。
	1980s - 国际标准化组织（ISO）发布OSI/RM，奠定了网络技术标准化的基础
	1990s - 英国人蒂姆·伯纳斯-李发明了图形化的浏览器，浏览器的简单易用性使得计算机网络迅速被普及。
	
TCP/IP模型
实现网络通信的基础是网络通信协议，这些协议通常是由互联网工程任务组 （IETF）制定的。所谓“协议”就是
通信计算机双方必须共同遵从的一组约定，例如怎样建立连接、怎样互相识别等，网络协议的三要素是：语
法、语义和时序。构成我们今天使用的Internet的基础的是TCP/IP协议族，所谓协议族就是一系列的协议及其
构成的通信模型，我们通常也把这套东西称为TCP/IP模型。与国际标准化组织发布的OSI/RM这个七层模型不
同，TCP/IP是一个四层模型，也就是说，该模型将我们使用的网络从逻辑上分解为四个层次，自底向上依次
依次是：网络接口层、网络层、传输层和应用层，如下图所示。
————————————————————————————————————————————————————————————————————————————————
IP通常被翻译为网际协议，它服务于网络层，主要实现了寻址和路由的功能。接入网络的每一台主机都需要有
自己的IP地址，IP地址就是主机在计算机网络上的身份标识。当然由于IPv4地址的匮乏，我们平常在家里、办
公室以及其他可以接入网络的公共区域上网时获得的IP地址并不是全球唯一的IP地址，而是一个局域网
（LAN）中的内部IP地址，通过网络地址转换（NAT）服务我们也可以实现对网络的访问。计算机网络上有大
量的被我们称为“路由器”的网络中继设备，它们会存储转发我们发送到网络上的数据分组，让从源头发出的数
据最终能够找到传送到目的地通路，这项功能就是所谓的路由。
——————————————————————————————————————————————————————————————————————————————
TCP全称传输控制协议，它是基于IP提供的寻址和路由服务而建立起来的负责实现端到端可靠传输的协议，之
所以将TCP称为可靠的传输协议是因为TCP向调用者承诺了三件事情：
	数据不传丢不传错（利用握手、校验和重传机制可以实现）。
	流量控制（通过滑动窗口匹配数据发送者和接收者之间的传输速度）。
	拥塞控制（通过RTT时间以及对滑动窗口的控制缓解网络拥堵）。
——————————————————————————————————————————————————————————————————————————————
网络应用模型
1、C/S模式和B/S模式。这里的C指的是Client（客户端），通常是一个需要安装到某个宿主操作系统上的应
用程序；而B指的是Browser（浏览器），它几乎是所有图形化操作系统都默认安装了的一个应用软件；通
过C或B都可以实现对S（服务器）的访问。关于二者的比较和讨论在网络上有一大堆的文章。
2、去中心化的网络应用模式。不管是B/S还是C/S都需要服务器的存在，服务器就是整个应用模式的中心，而
去中心化的网络应用通常没有固定的服务器或者固定的客户端，所有应用的使用者既可以作为资源的提供
者也可以作为资源的访问者。
——————————————————————————————————————————————————————————————————————————————
基于HTTP协议的网络资源访问
HTTP（超文本传输协议）
HTTP是超文本传输协议（Hyper-Text Transfer Proctol）的简称，维基百科上对HTTP的解释是：超文本传输协
议是一种用于分布式、协作式和超媒体信息系统的应用层协议，它是万维网数据通信的基础，设计HTTP最初
的目的是为了提供一种发布和接收HTML页面的方法，通过HTTP或者HTTPS（超文本传输安全协议）请求的资
源由URI（统一资源标识符）来标识。关于HTTP的更多内容，我们推荐阅读阮一峰老师的《HTTP 协议入
门》，简单的说，通过HTTP我们可以获取网络上的（基于字符的）资源，开发中经常会用到的网络API（有
地方也称之为网络数据接口）就是基于HTTP来实现数据传输的。

JSON格式
JSON（JavaScript Object Notation）是一种轻量级的数据交换语言，该语言以易于让人阅读的文字（纯文
本）为基础，用来传输由属性值或者序列性的值组成的数据对象。尽管JSON是最初只是Javascript中一种创建
对象的字面量语法，但它在当下更是一种独立于语言的数据格式，很多编程语言都支持JSON格式数据的生成
和解析，Python内置的json模块也提供了这方面的功能。由于JSON是纯文本，它和XML一样都适用于异构系
统之间的数据交换，而相较于XML，JSON显得更加的轻便和优雅。下面是表达同样信息的XML和JSON，而
JSON的优势是相当直观的。

XML的例子：
<?xml version="1.0" encoding="UTF-8"?>
<message>
	<from>Alice</from>
	<to>Bob</to>
	<content>Will you marry me?</content>
</message>

JSON的例子：
{
    "from": "Alice",
    "to": "Bob",
    "content": "Will you marry me?"
}
——————————————————————————————————————————————————————————————————————————————————
requests库
requests是一个基于HTTP协议来使用网络的第三库，其官方网站有这样的一句介绍它的话：“Requests是唯一
的一个非转基因的Python HTTP库，人类可以安全享用。”简单的说，使用requests库可以非常方便的使用
HTTP，避免安全缺陷、冗余代码以及“重复发明轮子”（行业黑话，通常用在软件工程领域表示重新创造一个已
有的或是早已被优化過的基本方法）。前面的文章中我们已经使用过这个库，下面我们还是通过requests来实
现一个访问网络数据接口并从中获取美女图片下载链接然后下载美女图片到本地的例子程序，程序中使用了天
行数据提供的网络API
——————————————————————————————————————————————————————————————————————————————————
"""
'''
from time import time
from threading import Thread

import requests

# 继承Thread类创建自定义的线程类
class DownloadHandler(Thread):
	def __init__(self,url):
		super().__init__()
		self.url = url
	def run(self):
		filename = self.url[self.url.rfind('/')+1:]
		resp = requests.get(self.url)
		with open('./'+filename, 'wb') as f:
			f.write(resp.content)
def main():
	# 通过requests模块的get函数获取网络资源
	# 下面的代码适用天行数据接口提供的网络API
	resp = requests.get('http://api.tianapi.com/meinv/?key=<KEY>&num=10')
	# 将服务器返回的json格式的数据解析为字典
	data_model = resp.json()
	print(data_model)
	for mm_dict in data_model['newslist']:
		url = mm_dict['picUrl']
		DownloadHandler(url).start()
if __name__ == '__main__':
	main()
'''

# 基于传输层协议的套接字编程
"""
套接字这个词对很多不了解网络编程的人来说显得非常晦涩和陌生，其实说得通俗点，套接字就是一套用C语
言写成的应用程序开发库，主要用于实现进程间通信和网络编程，在网络应用开发中被广泛使用。在Python中
也可以基于套接字来使用传输层提供的传输服务，并基于此开发自己的网络应用。实际开发中使用的套接字可
以分为三类：流套接字（TCP套接字）、数据报套接字和原始套接字。
"""

# TCP套接字
"""
所谓TCP套接字就是使用TCP协议提供的传输服务来实现网络通信的编程接口。在Python中可以通过创建
socket对象并指定type属性为SOCK_STREAM来使用TCP套接字。由于一台主机可能拥有多个IP地址，而且很有
可能会配置多个不同的服务，所以作为服务器端的程序，需要在创建套接字对象后将其绑定到指定的IP地址和
端口上。这里的端口并不是物理设备而是对IP地址的扩展，用于区分不同的服务，例如我们通常将HTTP服务
跟80端口绑定，而MySQL数据库服务默认绑定在3306端口，这样当服务器收到用户请求时就可以根据端口号
来确定到底用户请求的是HTTP服务器还是数据库服务器提供的服务。端口的取值范围是0~65535，而1024以
下的端口我们通常称之为“著名端口”（留给像FTP、HTTP、SMTP等“著名服务”使用的端口，有的地方也称之
为“周知端口”），自定义的服务通常不使用这些端口，除非自定义的是HTTP或FTP这样的著名服务。
"""

"""下面的代码实现了一个提供时间日期的服务器。"""
'''
from socket import socket,SOCK_STREAM,AF_INET
from datetime import datetime

def main():
	# 1、创建套接字对象并指定使用哪种传输服务
	# family = AF_INET   -IPv4地址
	# family = AF_INET6  -IPv6地址
	# type = SOCK_STREAM   -tcp套接字
	# type = SOCK_DGRAM    -udp套接字
	# type = SOCK_RAW    -原始套接字
	server = socket(family=AF_INET,type=SOCK_STREAM)
	# 2、绑定IP地址和端口号(端口用于区分不同的服务)
	# 同一时间在同一个端口上只能绑定一个服务否则报错
	server.bind(('127.0.0.1', 6789))
	# 3、开启监听-监听客户端连接到服务器
	# 参数512可以理解为链接队列的大小
	server.listen(512)
	print('服务器启动，开始监听...')
	while True:
		# 4、通过循环接收客户端的连接并做出相应的处理(提供服务)
		# accept方法是一个阻塞方法如果没有客户端连接到服务器代码不会向下执行
		# accept方法返回一个元组其中的第一个元素是客户端对象
		# 第二个元素是连接到服务器的客户端的地址(由IP和端口两部分构成)
		client,addr = server.accept()
		print(str(addr) + '连接到了服务器.')
		# 5、发送数据
		client.send(str(datetime.now()).encode('utf-8'))
		# 6、断开连接
		client.close()
main()
'''
"""
当然我们也可以通过Python的程序来实现TCP客户端的功能，相较于实现服务器程序，实现客户端程序就简单多了，代码如下所示。
"""
'''
from socket import socket

def main():
	# 1、创建套接字对象默认使用ipv4和tcp协议
	client = socket()
	# 2、连接到服务器(需要制定ip地址和端口)
	client.connect(('127.0.0.1',6789))
	# 3、从服务器接受数据
	print(client.recv(1024).decode('utf-8'))
	client.close()
main()
'''

# 多线程服务器
"""
需要注意的是，上面的服务器并没有使用多线程或者异步I/O的处理方式，这也就意味着当服务器与一个客户
端处于通信状态时，其他的客户端只能排队等待。很显然，这样的服务器并不能满足我们的需求，我们需要的
服务器是能够同时接纳和处理多个用户请求的。下面我们来设计一个使用多线程技术处理多个用户请求的服务
器，该服务器会向连接到服务器的客户端发送一张图片。
"""
'''
# 服务器端代码
from socket import socket,SOCK_STREAM,AF_INET
from base64 import b64encode
from json import dumps
from threading import Thread

def main():
	# 自定义线程类
	class FileTransferHandler(Thread):
		def __init__(self,cclient):
			super().__init__()
			self.cclient = client
		def run(self):
			my_dict = {}
			my_dict['filename'] = 'timg.jpg'
			# json是纯文本不能携带二进制数据
			# 所以图片的二进制数据要处理成base64编码
			my_dict['filedata'] = data
			# 通过dumps函数将字典处理成json字符串
			json_str = dumps(my_dict)
			# 发送json字符串
			self.cclient.send(json_str.encode('utf-8'))
			self.cclient.close()
	# 1、创建套接字对象并指定使用哪种传输服务
	server = socket()
	# 2、绑定ip地址和端口(区分不同服务)
	server.bind(('127.0.0.1',5566))
	# 3、开启监听 --监听客户端连接到服务器
	server.listen(512)
	print('服务器启动，开始监听...')
	with open('timg.jpg', 'rb') as f:
		# 将二进制数据处理成base64在解码成字符串
		data = b64encode(f.read()).decode('utf-8')
	while True:
		client,addr = server.accept()
		# 启动一个线程来处理客户端的请求
		FileTransferHandler(client).start()
if __name__ == '__main__':
	main()

# 客户端代码
from socket import socket
from json import loads
from base64 import b64decode

def main():
	client = socket()
	client.connect(('127.0.0.1', 5566))
	# 定义一个保存二进制数据的对象
	in_data = bytes()
	# 由于不知道服务器发送的数据由多大，每次接收1024字节
	data = client.recv(1024)
	while data:
		# 将收到的数据拼接起来
		in_data += data
		data = client.recv(1024)
	# 将收到的二进制数据解码成json字符串并转换为字典
	# loads函数的作用就是将json字符串转换成字典对象
	my_dict = loads(in_data.decode('utf-8'))
	filename = my_dict['filename']
	filedata = my_dict['filedata'].encode('utf-8')
	with open('副本'+filename, 'wb') as f:
		# 将base64格式的数据解码成二进制数据写入文件
		f.write(b64decode(filedata))
	print('图片已保存.')
if __name__ == '__main__':
	main()
'''
"""
在这个案例中，我们使用了JSON作为数据传输的格式（通过JSON格式对传输的数据进行了序列化和反序列
的操作），但是JSON并不能携带二进制数据，因此对图片的二进制数据进行了Base64编码的处理。Base64是
一种用64个字符表示所有二进制数据的编码方式，通过将二进制数据每6位一组的方式重新组织，刚好可以使
用0~9的数字、大小写字母以及“+”和“/”总共64个字符表示从000000到111111的64种状态。
"""

# UDP套接字
"""
传输层除了有可靠的传输协议TCP之外，还有一种非常轻便的传输协议叫做用户数据报协议，简称UDP。TCP
和UDP都是提供端到端传输服务的协议，二者的差别就如同打电话和发短信的区别，后者不对传输的可靠性和
可达性做出任何承诺从而避免了TCP中握手和重传的开销，所以在强调性能和而不是数据完整性的场景中（例
如传输网络音视频数据），UDP可能是更好的选择。可能大家会注意到一个现象，就是在观看网络视频时，有
时会出现卡顿，有时会出现花屏，这无非就是部分数据传丢或传错造成的。在Python中也可以使用UDP套接字
来创建网络应用，对此我们不进行赘述，有兴趣的读者可以自行研究。
"""

# 网络应用开发

# 发送电子邮件
"""
在即时通信软件如此发达的今天，电子邮件仍然是互联网上使用最为广泛的应用之一，公司向应聘者发出录用
通知、网站向用户发送一个激活账号的链接、银行向客户推广它们的理财产品等几乎都是通过电子邮件来完成
的，而这些任务应该都是由程序自动完成的。

就像我们可以用HTTP（超文本传输协议）来访问一个网站一样，发送邮件要使用SMTP（简单邮件传输协
议），SMTP也是一个建立在TCP（传输控制协议）提供的可靠数据传输服务的基础上的应用级协议，它规定
了邮件的发送者如何跟发送邮件的服务器进行通信的细节，而Python中的smtplib模块将这些操作简化成了几
个简单的函数。
"""

"""下面的代码演示了如何在Python发送邮件。"""
'''
from smtplib import SMTP
from email.header import Header
from email.mime.text import MIMEText

def main():
	sender = '<EMAIL>'
	receivers = ['<EMAIL>']
	message = MIMEText('用python发送邮件的实例代码.', 'plain','utf-8')
	message['From'] = Header('王大锤','utf-8')
	message['To'] = Header('王朝阳','utf-8')
	message['Subject'] = Header('示例代码实验邮件','utf-8')
	smtper = SMTP('smtp.qq.com')
	smtper.login(sender, 'hfmrcdivpxayejhb')
	smtper.sendmail(sender,receivers,message.as_string())
	print('邮件发送完成')
main()
'''

# 发送短信
"""
发送短信也是项目中常见的功能，网站的注册码、验证码、营销信息基本上都是通过短信来发送给用户的。在
下面的代码中我们使用了互亿无线短信平台（该平台为注册用户提供了50条免费短信以及常用开发语言发送短
信的demo，可以登录该网站并在用户自服务页面中对短信进行配置）提供的API接口实现了发送短信的服务，
当然国内的短信平台很多，读者可以根据自己的需要进行选择（通常会考虑费用预算、短信达到率、使用的难
易程度等指标），如果需要在商业项目中使用短信服务建议购买短信平台提供的套餐服务。
"""
'''
import urllib.parse
import http.client
import json

def main():
	host = "106.ihuyi.com"
	sms_send_uri = "/webservice/sms.php?method=Submit"
	params = urllib.parse.urlencode({'account':'C75138010','password':'<PASSWORD>','content':'您的验证码是：1234。请不要把验证码泄露给其他人。','mobile':'18729537656','format':'json'})
	print(params)
	headers = {'Content-type': 'application/x-www-form-urlencoded', 'Accept': 'text/plain'}
	conn = http.client.HTTPConnection(host,port=80,timeout=30)
	conn.request('POST',sms_send_uri,params,headers)
	response = conn.getresponse()
	response_str = response.read()
	jsonstr = response_str.decode('utf-8')
	print(json.loads(jsonstr))
	conn.close()
'''
main()

<file_sep># 图形和办公文档处理

"""
用程序来处理图像和办公文档经常出现在实际开发中，Python的标准库中虽然没有直接支持这些操作的模块，
但我们可以通过Python生态圈中的第三方模块来完成这些操作。

用pillow操作图象
Pillow是由从著名的Python图像处理库PIL发展出来的一个分支，通过Pillow可以实现图像压缩和
图像处理等各

Pillow中最为重要的是Image类，读取和处理图像都要通过这个类来完成。
"""
'''
from PIL import Image

def main():
	image = Image.open('./timg.jpg')
	print(image.format,image.size,image.mode)
	image.show()
main()
'''

# 1、裁剪图片
'''
from PIL import Image

image = Image.open('timg.jpg')
rect = 80,20,310,360
image.crop(rect).show()
'''

# 2、生成缩略图
'''
from PIL import Image

image = Image.open('timg.jpg')
size = 128,128
image.thumbnail(size)
image.show()
'''

# 3、缩放和黏贴图片
'''
from PIL import Image

image1 = Image.open('timg.jpg')
image2 = Image.open('猪妹.jpg')
rect = 100,20,200,260
pikaqiu_head = image2.crop(rect)
width,height = pikaqiu_head.size
image1.paste(pikaqiu_head.resize((int(width/1.5), int(height/1.5))),(172,40))
image1.show()
'''

# 4、旋转和翻转
'''
from PIL import Image

image = Image.open('猪妹.jpg')
image.rotate(180).show()
image.transpose(Image.FLIP_LEFT_RIGHT).show()
'''

# 5、操作像素
'''
from PIL import Image

image = Image.open('timg.jpg')
for x in range(80,310):
	for y in range(20,360):
		image.putpixel((x,y),(128,128,128))
image.show()
'''

# 6、滤镜效果
'''
from PIL import Image,ImageFilter

image = Image.open('timg.jpg')
image.filter(ImageFilter.CONTOUR).show() # 这里把图片变黑白了
'''

# 处理Excel电子表格
"""
Python的openpyxl模块让我们可以在Python程序中读取和修改Excel电子表格，当然实际工作中，我们可能会
用LibreOffice Calc和OpenOffice Calc来处理Excel的电子表格文件，这就意味着openpyxl模块也能处理来自这
些软件生成的电子表格。关于openpyxl的使用手册和使用文档可以查看它的官方文档。
链接：https://openpyxl.readthedocs.io/en/stable/#
"""

# 处理Word文档
"""
利用python-docx模块，Pytho 可以创建和修改Word文档，当然这里的Word文档不仅仅是指通过微软的
Office软件创建的扩展名为docx的文档，LibreOffice Writer和OpenOffice Writer都是免费的字处理软件。
"""

# 处理PDF文档
"""
PDF是Portable Document Format的缩写，使用.pdf作为文件扩展名。接下来我们就研究一下如何通过Python
实现从PDF读取文本内容和从已有的文档生成新的PDF文件。
"""
<file_sep># 字符串和正则表达式

# 使用正则表达式
"""
在编写处理字符串的程序或网页时，经常会有查找符合某些复杂规则的字符串的需要，正则表达式就是用于描述
这些规则的工具，换句话正则表达式是一种工具，它定义了字符串的匹配模式(检查一个字符串是否有跟某种模式
匹配的部分或者从一个字符串中将与模式匹配的部分提取出来或者替换掉)。
如果你在Windows操作系统中使用过文件查找并且在指定文件名时使用过通配符（*和?），
那么正则表达式也是与之类似的用来进行文本匹配的工具，只不过比起通配符正则表达
式更强大，它能更精确地描述你的需求（当然你付出的代价是书写一个正则表达式比打出一个通配
符要复杂得多，要知道任何给你带来好处的东西都是有代价的，就如同学习一门编程语言一样），
比如你可以编写一个正则表达式，用来查找所有以0开头，后面跟着2-3个数字，然后是一个连字
号“-”，最后是7或8位数字的字符串（像028-12345678或0813-7654321），这不就是国内的座机号
码吗。最初计算机是为了做数学运算而诞生的，处理的信息基本上都是数值，而今天我们在日常工
作中处理的信息基本上都是文本数据，我们希望计算机能够识别和处理符合某些模式的文本，正则
表达式就显得非常重要了。今天几乎所有的编程语言都提供了对正则表达式操作的支持，Python通
过标准库中的re模块来支持正则表达式操作。

我们可以考虑下面一个问题：我们从某个地方（可能是一个文本文件，也可能是网络上的一则新
闻）获得了一个字符串，希望在字符串中找出手机号和座机号。当然我们可以设定手机号是11位的
数字（注意并不是随机的11位数字，因为你没有见过“25012345678”这样的手机号吧）而座机号跟
上一段中描述的模式相同，如果不使用正则表达式要完成这个任务就会很麻烦。

关于正则表达式的相关知识，大家可以阅读一篇非常有名的博客叫《正则表达式30分钟入门教程》，
读完这篇文章后你就可以看懂下面的表格，这是我们对正则表达式中的一些基本符号进行的扼要总结。

不会查文档把
链接：https://github.com/jackfrued/Python-100-Days/blob/master/Day01-15/12.%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%92%8C%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F.md
"""

# python对正则表达式的支持
"""
Python提供了re模块来支持正则表达式相关操作，下面是re模块中的核心函数。
函数											 说明
compile(pattern, flags=0)					 编译正则表达式返回正则表达式对象
match(pattern, string, flags=0)				 用正则表达式匹配字符串 成功返回匹配对象 否则返回None
search(pattern, string, flags=0)			 搜索字符串中第一次出现正则表达式的模式 成功返回匹配对象 否则返回None
split(pattern, string, maxsplit=0, flags=0)	 用正则表达式指定的模式分隔符拆分字符串 返回列表
sub(pattern, repl, string, count=0, flags=0) 用指定的字符串替换原字符串中与正则表达式匹配的模式 可以用count指定替换的次数
fullmatch(pattern, string, flags=0)	match	 函数的完全匹配（从字符串开头到结尾）版本
findall(pattern, string, flags=0)			 查找字符串所有与正则表达式匹配的模式 返回字符串的列表
finditer(pattern, string, flags=0)	 	     查找字符串所有与正则表达式匹配的模式 返回一个迭代器
purge()										 清除隐式编译的正则表达式的缓存
re.I / re.IGNORECASE						 忽略大小写匹配标记
re.M / re.MULTILINE						 	 多行匹配标记
说明： 上面提到的re模块中的这些函数，实际开发中也可以用正则表达式对象的方法替代对这些函数
的使用，如果一个正则表达式需要重复的使用，那么先通过compile函数编译正则表达式并创建出
正则表达式对象无疑是更为明智的选择。
"""

# 例子1：验证输入用户名和qq号是否有效并给出对应的提示信息
"""
验证输入的用户名和qq号是否有效并给出对应的提示信息

要求：用户名必须由字母，数字或下划线构成且长度在6-20和字符之间,qq号是5-12的数字4
且首位不为0
"""
'''
import re

def main():
	username = input('请输入用户名：')
	qq = input('请输入qq号：')
	# match 函数的第一个参数是正则表达式字符串或正则表达式对象
	# 第二个参数是要跟正则表达式做匹配的字符串对象
	m1 = re.match(r'^[0-9a-zA-Z_]{6,20}$',username)
	if not m1:
		print('请输入有效的用户名.')
	m2 = re.match(r'^[1-9]\d{4,11}$',qq)
	if not m2:
		print('请输入有效的qq号.')
	if m1 and m2:
		print('你输入的信息有效的!')
main()
提示： 上面在书写正则表达式时使用了“原始字符串”的写法（在字符串前面加上了r），
所谓“原始字符串”就是字符串中的每个字符都是它原始的意义，说得更直接一点就是字符串中没有
所谓的转义字符啦。
'''

# 例子2：从一段文字内提取出国内手机号码
'''
import re


def main():
    # 创建正则表达式对象 使用了前瞻和回顾来保证手机号前后不应该出现数字
    pattern = re.compile(r'(?<=\D)1[34578]\d{9}(?=\D)')
    sentence = 
    重要的事情说8130123456789遍，我的手机号是13512346789这个靓号，
    不是15600998765，也是110或119，王大锤的手机号才是15600998765。
    
    # 查找所有匹配并保存到一个列表中
    mylist = re.findall(pattern, sentence)
    print(mylist)
    print('--------华丽的分隔线--------')
    # 通过迭代器取出匹配对象并获得匹配的内容
    for temp in pattern.finditer(sentence):
        print(temp.group())
    print('--------华丽的分隔线--------')
    # 通过search函数指定搜索位置找出所有匹配
    m = pattern.search(sentence)
    while m:
        print(m.group())
        # m = pattern.search(sentence, m.end())


if __name__ == '__main__':
    main()
'''

# 例子3：替换字符串中的不良内容
'''
import re

def main():
	sentence = '你丫是傻叉吗? 我操你大爷的.Fuck you.'
	purified = re.sub('[操肏艹]|fuck|shit|傻[比屄逼叉缺吊屌]|煞笔','*',sentence,flags=re.IGNORECASE)
	print(purified)
main()
'''

# 拆分长字符串
'''
import re
def main():
	poem = '窗前明月光，疑是地上霜、举头望明月，低头思故乡。'
	sentence_list = re.split(r'[，。.]',poem)
	while '' in sentence_list:
		sentence_list.remove('')
	print(sentence_list)
main()
'''


"""
后话
如果要从事爬虫类应用的开发，那么正则表达式一定是一个非常好的助手，因为它可以帮助我们迅速
的从网页代码中发现某种我们指定的模式并提取出我们需要的信息，当然对于初学者来收，
要编写一个正确的适当的正则表达式可能并不是一件容易的事情（当然有些常用的正则表达式可以
直接在网上找找），所以实际开发爬虫应用的时候，有很多人会选择Beautiful Soup或Lxml来进行匹配
和信息的提取，前者简单方便但是性能较差，后者既好用性能也好，但是安装稍嫌麻烦，这
些内容我们会在后期的爬虫专题中为大家介绍。
"""


<file_sep>import socket

# 1 买手机
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 2 插手机卡
phone.bind(('127.0.0.1', 9094)) # bind传入的是一个元祖

# 3 开机
phone.listen(5) # 代表最大挂起的链接数

# 4 等电话
print("start....")
conn, client_addr = phone.accept()
print(client_addr)

# 5 收发消息
while True: # 通信循环
	data = conn.recv(1024) # 1 单位: bytes 2 1024代表最大接收1024个字节
	print('客户端数据', data)

	conn.send(data.upper())

conn.close()

phone.close()
<file_sep>import socket
import subprocess
import struct

# 1 买手机
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

# 2 插手机卡
phone.bind(('127.0.0.1', 9094)) # bind传入的是一个元祖

# 3 开机
phone.listen(5) # 代表最大挂起的链接数

# 4 等电话
print("start....")

while True: # 链接循环
	conn, client_addr = phone.accept()
	print(client_addr)

	# 5 收发消息

	while True: # 通信循环
		# 客户端单方面断开链接，会导致服务端死循环
		try:
			# 1 接收命令
			cmd = conn.recv(8096) # 1 单位: bytes 2 1024代表最大接收1024个字节
			if not cmd: # 适用于Linux系统
				break
			
			# 2 执行命令， 拿到结果
			obj = subprocess.Popen(cmd.decode('utf-8'), shell=True,
									stdout=subprocess.PIPE, 
									stderr=subprocess.PIPE)
			stdout = obj.stdout.read()
			stderr = obj.stderr.read()
			
			# 3 把命令的结果返回给客户端
			
			# 第一步： 制作固定长度的报头
			total_size = len(stdout) + len(stderr)
			header = struct.pack('i', total_size)
			# 第二步：把报头 (固定长度) 发送给客户端	
			conn.send(header) 
			
			# 第三步：在发真实的数据
			conn.send(stdout)
			conn.send(stderr)
		except	ConnectionResetError:  #使用于windows系统
			break
	conn.close()

phone.close()
<file_sep>import socket
import subprocess

# 1 买手机
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

# 2 插手机卡
phone.bind(('127.0.0.1', 9094)) # bind传入的是一个元祖

# 3 开机
phone.listen(5) # 代表最大挂起的链接数

# 4 等电话
print("start....")

while True: # 链接循环
	conn, client_addr = phone.accept()
	print(client_addr)

	# 5 收发消息

	while True: # 通信循环
		# 客户端单方面断开链接，会导致服务端死循环
		try:
			# 1 接收命令
			cmd = conn.recv(1024) # 1 单位: bytes 2 1024代表最大接收1024个字节
			if not cmd: # 适用于Linux系统
				break
			
			# 2 执行命令， 拿到结果
			obj = subprocess.Popen(cmd.decode('utf-8'), shell=True,
									stdout=subprocess.PIPE, 
									stderr=subprocess.PIPE)
			stdout = obj.stdout.read()
			stderr = obj.stderr.read()
			
			# 3 把命令的结果返回给客户端
			print(len(stdout+stderr)) 
			conn.send(stdout+stderr) # + 是一个可以优化的点
		except	ConnectionResetError:  #使用于windows系统
			break
	conn.close()

phone.close()
<file_sep>import sys

import pygame
from bullet import Bullet 
from alien import Alien   
from time import sleep


def check_keydown_events(event,ai_settings, screen, ship, bullets):
	"""响应按键"""
	if event.key == pygame.K_RIGHT:
		ship.moving_right = True
	elif event.key == pygame.K_LEFT:
		ship.moving_left = True
	elif event.key == pygame.K_SPACE:
		fire_bullet(ai_settings, screen, ship, bullets)
	elif event.key == pygame.K_q:
		sys.exit()

		
def fire_bullet(ai_settings, screen,ship,bullets):
	"""如果没有到达限制，就发射一颗子弹"""
	#创建新子弹，并将其加入编组bullets中
	if len(bullets) < ai_settings.bullet_allowed:
		new_bullet = Bullet(ai_settings, screen, ship)
		bullets.add(new_bullet)

		
def check_keyup_events(event, ship):
	"""响应松开"""
	if event.key == pygame.K_RIGHT:
		ship.moving_right = False
	elif event.key == pygame.K_LEFT:
		ship.moving_left = False

		
def check_events(ai_settings, screen, ship, bullets):
	"""响应按键和鼠标事件"""
	for event in pygame.event.get():
		if event.type == pygame.QUIT:
			sys.exit()
		
		elif event.type == pygame.KEYDOWN:
			check_keydown_events(event,ai_settings, screen, ship, bullets)
		
		elif event.type == pygame.KEYUP:
			check_keyup_events(event, ship)

						
def update_screen(ai_settings, screen,stats,  ship,aliens, bullets):
	""""更新屏幕上的图像，并切换到新屏幕"""
	
	#每次循环时都会重新绘制屏幕
	screen.fill(ai_settings.bg_color)
	
	#在飞船和外星人后面重绘所有子弹
	for bullet in bullets.sprites():
		bullet.draw_bullet()
	
	ship.blitme()
	aliens.draw(screen)
	
	#如果游戏处于非活动状态， 就绘制Play按钮
	#if not stats.game_active:
		#play_button.draw_button()
		
		
	#让最近绘制的屏幕可见
	pygame.display.flip()


def check_bullet_alien_collisions(ai_settings, screen, ship, aliens, bullets):
	"""相应子弹和外星人的碰撞"""
	#删除发生碰撞的子弹和外星人
	collections = pygame.sprite.groupcollide(bullets, aliens, True, True)
	
	if len(aliens) == 0:
		bullets.empty()
		ai_settings.increase_speed()
		creat_fleet(ai_settings, screen, ship, aliens)
	"""（要模拟能够穿行到屏幕顶端的高能子弹 —— 消灭它击中的每个外星人，
	    可将第一个布尔实参设置为 False ，并让第二个布尔实参为 True 。
	    这样被击中的外星人将消失，但所有的子弹都始终有效，直到抵达屏幕顶端后消失。）"""
	    
	    	
def update_bullets(ai_settings, screen, ship, aliens, bullets):
	"""更新子弹位置，并删除已经消失的子弹"""
	#更新子弹位置
	bullets.update()
	
	
	#删除已消失子弹位置
	for bullet in bullets.copy():
		if bullet.rect.bottom <= 0:
			bullets.remove(bullet)
	#print(len(bullets))
	#检查是否有子弹击中了外星人
	#如果是这样，就删除相应的子弹和外星人
	check_bullet_alien_collisions(ai_settings, screen, ship, aliens, bullets)
	
	
def get_number_aliens_x(ai_settings, alien_width):
	"""并计算一行可以容纳多少个外星人"""
	available_space_x = ai_settings.screen_width -2*alien_width
	number_aliens_x = int(available_space_x /(2*alien_width))
	return number_aliens_x


def get_number_rows(ai_settings, ship_height, alien_height):
	"""计算屏幕可以容纳多少个外星人"""
	available_space_y = (ai_settings.screen_height - (3*alien_height)-ship_height)
	numbers_rows = int(available_space_y /(2*alien_height))
	return numbers_rows

	
def creat_alien(ai_settings, screen, aliens, alien_number, row_number):
	"""创建一个外星人并将其加入当前行"""
	alien = Alien(ai_settings, screen)
	alien_width = alien.rect.width
	alien.x = alien_width + 2*alien_width*alien_number
	alien.rect.x = alien.x
	alien.rect.y = alien.rect.height + 2*alien.rect.height*row_number
	aliens.add(alien)

	
def creat_fleet(ai_settings, screen, ship, aliens):
	"""创建外星人群"""
	#创建一个外星人，并计算一行可以容纳多少个外星人
	alien = Alien(ai_settings, screen)
	number_aliens_x = get_number_aliens_x(ai_settings, alien.rect.width)
	number_rows = get_number_rows(ai_settings, ship.rect.height,alien.rect.height)
	
	#创建第一行外星人
	for row_number in range(number_rows):
		for alien_number in range(number_aliens_x):
			creat_alien(ai_settings, screen, aliens, alien_number, row_number)


def check_fleet_edges(ai_settings, aliens):
	"""有外星人到达边缘是采取相应措施"""
	for alien in aliens.sprites():
		if alien.check_edges():
			change_fleet_direction(ai_settings, aliens)
			break


def change_fleet_direction(ai_settings, aliens):
	"""将整群外星人下移，并改变他们的方向"""
	for alien in aliens.sprites():
		alien.rect.y += ai_settings.fleet_drop_speed
	ai_settings.fleet_direction *= -1


def ship_hit(ai_settings, stats, screen, ship, aliens, bullets):
	"""相应被外星人撞到的飞船"""
	if stats.ships_left > 0:
		#将ship_left减1
		stats.ships_left -= 1
		
		#清空外星人列表和子弹列表
		aliens.empty()
		bullets.empty()
		
		#创建一群新的外星人， 并将飞船放到屏幕低端中央
		creat_fleet(ai_settings, screen, ship, aliens)
		ship.center_ship()
		
		#暂停
		sleep(0.5)
	
	else:
		stats.game_active = False
		


def check_aliens_bottom(ai_settings, stats, screen, ship, aliens, bullets):
	"""检查是否有外星人到达了屏幕底端"""
	screen_rect = screen.get_rect()
	for alien in aliens.sprites():
		if alien.rect.bottom >= screen_rect.bottom:
			#像飞船被撞到一样处理
			ship_hit(ai_settings, stats, screen, ship, aliens, bullets)
			break
			
						
def update_aliens(ai_settings,stats, screen, ship, aliens, bullets):
	"""检查是否有外星人位于屏幕边缘， 更新外星人群中所有外星人的位置"""
	check_fleet_edges(ai_settings,aliens)
	aliens.update()
	
	#检测外星人和飞船之间的碰撞
	if pygame.sprite.spritecollideany(ship, aliens):
		ship_hit(ai_settings, stats, screen, ship, aliens, bullets)
	
	#检查是否有外星人到达屏幕底端
	check_aliens_bottom(ai_settings, stats, screen, ship, aliens, bullets)
	
<file_sep># 面向对象编程基础

"""
活在当下的程序员应该都听过“面向对象编程”一词，也经常有人问能不能用一句话解释下什么是
“面向对象编程”，我们来看比较正式的说法。
	把一组数据和处理他们的方法组成对象，把相同行为的对象归纳为类，通过类的封装，隐藏
	内部细节，通过继承实现类的特化和泛化，通过多态实现基于对象类型的动态分配。
————————————————————————————————————————————————————————————————————————
	封装：不管你是土鳖还是土豪，不管你中午吃的是窝头还是鲍鱼，你的下水都在你的肚皮里，别
人看不到你中午吃了啥，除非你自己说给他们听(或者画给他们看)
	继承：刚说了，你个土鳖/豪，你们全家都是土豪/鳖。冰冻三尺非一日之寒，你有今天，必定可以
从你爸爸爷爷那里追根溯源。正所谓虎父无犬子，正恩同学那么狠，他爹正日就不是什么善茬，更甭说
他爷爷。
多态：哲学家说过，世上不会有两个一模一样的双胞胎。及及时你从你父亲那里继承来的土鳖/土豪气质
也完全不可能是从一个模子里刻出来的，总会有些差别。比如你爸爸喜欢蹲在门前面吃面，你喜欢骑
在村口的歪脖子树上吃，或者反过来。当然，也可能令尊喜欢吃鲍鱼时旁边有几个艺校小女生喝酒
唱歌助兴，你可能喜欢弄个街舞乐队来吹拉弹唱。
————————————————————————————————————————————————————————————————————————

之前我们说过“程序是指令的集合”，我们在程序中书写的语句在执行时会变成一条或多条指令然后由
CPU去执行。当然为了简化程序的设计，我们引入了函数的概念，把相对独立且经常重复使用的代码
放置到函数中，在需要使用这些功能的时候只要调用函数即可；如果一个函数的功能过于复杂和臃肿
我们又可以进一步将函数继续切分为子函数来降低系统的复杂性。但是说了这么多，不知道大家
是否发现，所谓编程就是程序员按照计算机的工作方式控制计算机完成各种任务。但是，计算机的
工作方式与正常人类的思维模式是不同的，如果编程就必须得抛弃人类正常的思维方式去迎合计算机
编程的乐趣就少了很多，“每个人都应该学习编程”这样的豪言壮语就只能说说而已。当然，这些还不
是最重要的，最重要的是当我们需要开发一个复杂的系统时，代码的复杂性会让开发和维护工作都变
得举步维艰，所以在上世纪60年代末期，“软件危机”、“软件工程”等一系列的概念开始在行业中出现。

当然，程序员圈子内的人都知道，现实中并没有解决上面所说的这些问题的“银弹”，真正让软件
开发者看到希望的是上世纪70年代诞生的Smalltalk编程语言中引入的面向对象的编程思想
（面向对象编程的雏形可以追溯到更早期的Simula语言）。按照这种编程理念，
程序中的数据和操作数据的函数是一个逻辑上的整体，我们称之为“对象”，而我们解决问题的方式
就是创建出需要的对象并向对象发出各种各样的消息，多个对象的协同工作最终可以让我们构造出
复杂的系统来解决现实中的问题。

说明： 当然面向对象也不是解决软件开发中所有问题的最后的“银弹”，所以今天的高级程序
设计语言几乎都提供了对多种编程范式的支持，Python也不例外。
———————————————————————————————————————————————————————————————————————
"""

# 类和对象
"""
简单的说，类是对象的蓝图和模板，而对象是类的实例。从这句话我们可以看出，类是抽象的概念
而对象是具体的东西。在面向对象编程的世界中，一切皆为对象，对象都有属性和行为，每个对象
都是独一无二的，而且对象是独一无二的，而且对象一定属于某个类(型)。当我们把一大堆拥有共同
特征的对象的静态特征(属性)和动态特征(行为)都抽取出来，就可以定义出一个叫做"类的东西"。
"""

# 定义类
'''
"""
在python中使用 class 关键字定义类，然后在类中通过函数来定义方法，这样就可以将对象的
动态特征描述出来。
"""

class Student(object):
	# __init__是一个特殊方法用于在创建对象时进行初始化操作
	# 通过这个方法我们可以为学生对象绑定nam和age两个属性
	def __init__(self,name,age):
		self.name = name
		self.age = age
	def study(self,course_name):
		print('%s正在学习%s.'%(self.name, course_name))
	# PEP 8要求标识符的名字用全小写，多个单词时下划线连接
	# 但是很多程序员和公司更倾向与使用驼峰命名法
	def watch_av(self):
		if self.age < 18:
			print('%s只能观看《熊出没》.' % self.name)
		else:
			print('%s正在观看到岛国爱情片.' % self.name)
# 说明：写在类中的函数，我们通常称之为(对象的方法),这些方法就是对象可以接受到的消息
'''
# 创建和使用对象
'''
def main():
	# 创建学生对象并且指定姓名和年龄
	stu1 = Student('王朝阳',22)
	# 给对象发study消息
	stu1.study('python程序设计')
	# 给对象发watch_av消息
	stu1.watch_av()
	stu2 = Student('王大锤',15)
	stu2.study('思想品德')
	stu2.watch_av()
main()
'''

# 访问可见性的问题
'''
"""
对于上面的代码，有C++、Java、C#等编程经验的程序员可能会问，我们给Student对象绑定
的name和age属性到底具有怎样的访问权限（也称为可见性）。因为在很多面向对象编程语言中，
我们通常会将对象的属性设置为私有的（private）或受保护的（protected），
简单的说就是不允许外界访问，而对象的方法通常都是公开的（public），因为公开的方法就是
对象能够接受的消息。在Python中，属性和方法的访问权限只有两种，也就是公开的和私有的，
如果希望属性是私有的，在给属性命名时可以用两个下划线作为开头，下面的代码可以验证这一点。
"""
class Test:
	def __init__(self,foo):
		self.__foo = foo
	def __bar(self):
		print(self.__foo)
		print('__bar')

def main():
	test = Test('hello')
	test.__bar() # AttributeError: 'Test' object has no attribute '__bar'
	print(test.__foo) # AttributeError: 'Test' object has no attribute '__foo'
main()
'''

"""
但是，Python并没有从语法上严格保证私有属性或方法的私密性，它只是给私有的属性和方法换了
一个名字来“妨碍”对它们的访问，事实上如果你知道更换名字的规则仍然可以访问到它们，
下面的代码就可以验证这一点。之所以这样设定，可以用这样一句名言加以解释，
就是“We are all consenting adults here”。因为绝大多数程序员都认为开放比封闭要好，
而且程序员要自己为自己的行为负责。
"""

"""
class Test:
	def __init__(self,foo):
		self.__foo = foo
	def __bar(self):
		print(self.__foo)
		print('__bar')
def main():
	test = Test('hello')
	test._Test__bar()
	print(test._Test__foo)
main()
"""

"""
在实际开发中，我们并不建议将属性设置为私有的，因为这样会导致子类无法访问。所以大多数python
程序员会遵循一种命名习惯就是让属性以单下划线开头来表示属性是受保护的，本类之外的代码在
访问这样的属性时应该保持慎重。这种做法不是语法上的规则，单下划线的属性和方法外界仍然是可以
访问的，所以更多的时候它是一种暗示。
"""

# 面向对象的支柱
"""
面向对象有三大支柱：封装，继承和多态。我自己对封装的理解是“隐藏一切可以隐藏的实现细节，
只向外界暴露（提供）简单的编程接口”。我们在类中定义的方法其实就是把数据和对数据的
操作封装起来了，在我们创建了对象之后，只需要给对象发送一个消息（调用方法）就可以执行方法
中的代码，也就是说我们只需要知道方法的名字和传入的参数（方法的外部视图），而不需要知道
方法内部的实现细节（方法的内部视图）。
"""
"""——————————————————————————————————————————————————————————————————"""

# 练习1：定义一个类描述数字时钟
'''
import time
import os
class Clock(object):
	"""数字时钟"""
	def __init__(self,hour=0,minute=0,second=0):
		"""初始化方法
		:param hour:时
		:param minute：分
		:param second: 秒
		"""
		self._hour = hour
		self._minute = minute
		self._second = second
	def run(self):
		"""走字"""
		self._second += 1
		if self._second == 60:
			self.second = 0
			self._minute += 1
			if self._minute == 60:
				self.minute = 0
				self._hour += 1
				if self._hour == 24:
					self._hour = 0
	def show(self):
		"""显示时间"""
		return '%02d:%02d:%02d' % (self._hour,self._minute,self._second)

def main():
	clock = Clock(23,58,00)
	while True:
		print(clock.show())
		time.sleep(1)
		clock.run()
main()
'''

# 练习2：定义一个类描述平面上的并提供移动点和计算到另外一个点的距离的方法
'''
from math import sqrt
class Point(object):
	def __init__(self,x=0,y=0):
		"""初始化方法
		:param x: 横坐标
		:param y: 纵坐标
		"""
		self.x = x
		self.y = y
	def move_to(self,x,y):
		"""移动到指定位置
		:param x: 新的横坐标
		:param x: 新的纵坐标
		"""
		self.x = x
		self.y = y
	def move_by(self,dx,dy):
		"""移动到指定增量
		:param dx: 横坐标的增量
		:param dy: 纵坐标的增量
		"""
		self.x += dx
		self.y += dy
	def distance_to(self,other):
		"""计算与另外一个点的距离
		:param otner: 另一个点
		"""
		dx = self.x - other.x
		dy = self.y - other.y
		return sqrt(dx**2 + dy**2)
	def __str__(self):
		return '(%s,%s)' % (str(self.x),str(self.y))
def main():
	p1 = Point(3,5)
	p2 = Point()
	print(p1)
	print(p2)
	p2.move_by(-1,2)
	print(p2)
	print(p1.distance_to(p2))
main()
'''
<file_sep># Python语言进阶

# 1、数据结构与算法
"""
算法：解决问题的方法和步骤
评价算法的好坏：渐进时间复杂度和空间复杂度。
渐进时间复杂度的大O标记：
	
	- 常量时间复杂度 - 布隆过滤器 / 哈希存储
	- 对数时间复杂度 - 折半查找（二分查找）
	- 线性时间复杂度 - 顺序查找 / 桶排序
	- 对数线性时间复杂度 - 高级排序算法（归并排序、快速排序）
	- 平方时间复杂度 - 简单排序算法（选择排序、插入排序、冒泡排序）
	- 立方时间复杂度 - Floyd算法 / 矩阵乘法运算
	- 几何级数时间复杂度 - 汉诺塔
	- 阶乘时间复杂度 - 旅行经销商问题 - NP
"""

"""排序算法(选择，冒泡，归并)和查找算法(顺序和折半)"""

"""简单选择排序"""
'''
def select_sort(origin_items,comp=lambda x,y: x < y):
	""""每次都去剩余的部分找最小的往前放"""
	items = origin_items[:]
	for i in range(len(items)-1):
		min_index = i
		for j in range(i+1, len(items)):
			if comp(items[j],items[min_index]):
				min_index = j
		items[i],items[min_index] = items[min_index],items[i]
	return items
def main():
	a = [9,8,7,6,5,4,3,2,1]
	b = select_sort(a)
	print(a)
	print(b)
'''

"""高质量冒泡排序(搅拌排序)"""
'''
def bubble_sort(origin_items, comp=lambda x,y: x>y):
	items = origin_items[:]
	for i in range(len(items)-1):
		swapped = False
		for j in range(i, len(items)-1-i):
			if comp(items[j], items[j+1]):
				items[j],items[j+1] = items[j+1],items[j]
				swapped = True
		if swapped:
			swapped = False
			for j in range(len(items)-2-i, i,-1):
				if comp(items[j-1],items[j]):
					items[j],items[j-1] = items[j-1],items[j]
					swapped = True
		if not swapped:
			break
	return items

import datetime
from random import randint

def main():
	a = []
	for i in range(1,10000):
		a.append(randint(1,1000))
	start = datetime.datetime.now()
	b = select_sort(a)
	b = bubble_sort(a)
	end = datetime.datetime.now()
	print(end-start)
main()
'''

"""归并排序(分治法)"""
'''
def merge_sort(items,comp=lambda x,y : x >= y):
	if len(items) < 2:
		return items[:]
	mid = len(items) // 2
	left = merge_sort(items[:mid],comp)
	right = merge_sort(items[mid:],comp)
	return merge(left,right,comp)

def merge(items1,items2,comp):
	items = []
	index1,index2 = 0,0
	while index1 < len(items1) and index2 < len(items2):
		if comp(items1[index1],items2[index2]):
			items.append(items1[index1])
			index1 += 1
		else:
			items.append(items2[index2])
			index2 += 1
	items += items1[index1:]
	items += items2[index2:]
	return items

def main():
	l = [4,3,2,1]
	ll = merge_sort(l,lambda x,y : x <= y)
	print(ll)
main()
'''

"""顺序查找"""
'''
def seq_search(items,key):
	for index,item in enumerate(items):
		if item == key:
			return index
	return -1
'''

"""折半查找"""
'''
def bin_search(items,key):
	start,end = 0,len(items)-1
	while start <= end:
		mid = (start + end) // 2
		if key > items[mid]:
			start = mid + 1
		elif key < items[mid]:
			end = mid - 1
		else:
			return mid
	return -1
'''

"""使用生成式(推导式)语法"""
'''
prices = {
    'AAPL': 191.88,
    'GOOG': 1186.96,
    'IBM': 149.24,
    'ORCL': 48.44,
    'ACN': 166.89,
    'FB': 208.09,
    'SYMC': 21.29
}
# 用股票价格大于100元的股票构造一个新的字典
prices2 = {key: value for key,value in prices.items() if value > 100}
print(prices2)
"""说明：生成式(推导式)可以用来生成列表，集合和字典"""
'''

"""嵌套的列表"""
'''
names= ['关羽','张飞','赵云','马超','黄忠']
courses = ['语文','数学','英语']
"""录入五个学生三门课的成绩"""
sorces = [[None] * len(courses) for _ in range(len(names))]
# print(sores)
for row,name in enumerate(names):
	for col,course in enumerate(courses):
		sorces[row][col] = float(input(f'请输入{name}的{course}成绩：'))
		print(sorces)
'''

"""heapq,itertools等的用法"""
"""
从列表中找出最大的或最小的N个元素
堆结构(大根堆，小根堆)
"""
'''
import heapq

list1 = [34, 25, 12, 99, 87, 63, 58, 78, 88, 92]
list2 = [
    {'name': 'IBM', 'shares': 100, 'price': 91.1},
    {'name': 'AAPL', 'shares': 50, 'price': 543.22},
    {'name': 'FB', 'shares': 200, 'price': 21.09},
    {'name': 'HPQ', 'shares': 35, 'price': 31.75},
    {'name': 'YHOO', 'shares': 45, 'price': 16.35},
    {'name': 'ACME', 'shares': 75, 'price': 115.65}
]
print(heapq.nlargest(3,list1))
print(heapq.nsmallest(3,list1))
print(heapq.nlargest(2,list2,key=lambda x: x['price']))
print(heapq.nsmallest(2,list2,key=lambda x: x['shares']))
'''

"""迭代工具-排列/组合/笛卡尔积"""
'''
import itertools
print(list(itertools.permutations('ABCD')))
print(list(itertools.combinations('ABCDE',3)))
print(list(itertools.product('ABCD','123')))
'''

"""collections模块下的工具类"""
'''
from collections import Counter

words = [
    'look', 'into', 'my', 'eyes', 'look', 'into', 'my', 'eyes',
    'the', 'eyes', 'the', 'eyes', 'the', 'eyes', 'not', 'around',
    'the', 'eyes', "don't", 'look', 'around', 'the', 'eyes',
    'look', 'into', 'my', 'eyes', "you're", 'under'
]
counter = Counter(words)
print(counter.most_common(3))
# [('eyes', 8), ('the', 5), ('look', 4)]
'''

"""常用算法"""
"""
1、穷举法-又又称暴力破解法。对所有的可能性进行验证，直到找到正确答案
2、贪婪发-在对问题求解时，总是做出在当前看来最好的选择，不追求最优解，快速找到满意解
3、分治法-把一个复杂的问题分成两个或更多的相同或相似的子问题，在把子问题分成更小的子问题
		直到可以直接求解的程度，最后将子问题的解进行合并得到原问题的解
4、回溯法-回溯法又称试探法，按选优条件向前搜索，当搜索到某一步发现原先选择并不优或达不到
		目标时，就退一步重新选择
5、动态规划-基本思想是将带求解问题分成若干个子问题，先求解并保存这些子问题的解，避免产生
		大量的重复计算
"""

"""穷举法例子"""
'''
# 公鸡5元一只 母鸡3元一只 小鸡1元三只
# 用100元买100只鸡 问公鸡/母鸡/小鸡各多少只
for x in range(20):
	for y in range(33):
		z = 100-x-y
		if 5*x + 3*y + z//3 == 100 and z%3 == 0:
			print(x,y,z)
'''

'''
# A、B、C、D、E五人在某天夜里合伙捕鱼 最后疲惫不堪各自睡觉
# 第二天A第一个醒来 他将鱼分为5份 扔掉多余的1条 拿走自己的一份
# B第二个醒来 也将鱼分为5份 扔掉多余的1条 拿走自己的一份
# 然后C、D、E依次醒来也按同样的方式分鱼 问他们至少捕了多少条鱼

fish = 6

while True:
	total = fish
	enough = True
	for _ in range(5):
		if(total-1)%5 == 0:
			total = (total-1)//5*4
		else:
			enough = False
			break
	if enough:
		print(fish)
		break
	fish += 5
'''

"""贪婪法例子"""
"""
假设小偷有一个背包，最多能装20公斤赃物，他闯入一户人家，发现如下表所示的物品。很显然，
他不能把所有物品都装入背包，所以确定拿走那些物品，留下哪些

名称	  价格（美元）	重量（kg）
电脑	  200	        20
收音机 20	        4
钟	  175	        10
花瓶	  50	        2
书	  10	        1
油画	  90	        9
______________________________________________________________________
贪婪法在求解问题时，总是做出在当前看来最好的选择，不追求最优解，快速找到满意解，。
"""
"""
class Thing(object):
	# 物品
	def __init__(self,name,price,weight):
		self.name = name
		self.price = price
		self.weight = weight
	@property
	def value(self):
	# 价格重量比
		return self.price / self.weight

def input_thing():
	# 输入物品信息
	name_str,price_str,weight_str = input().split()
	return name_str,int(price_str),int(weight_str)

def main():
	# 主函数
	max_weight,num_of_things = map(int, input().split())
	all_things = []
	for _ in range(num_of_things):
		all_things.append(Thing(*input_thing()))
	all_things.sort(key=lambda x: x.value,reverse=True)
	total_weight = 0
	total_price = 0
	for thing in all_things:
		if total_weight + thing.weight <= max_weight:
			print(f'小偷拿走了{thing.name}')
			total_weight += thing.weight
			total_price += thing.price
	print(f'总价值:{total_price}美元')
main()
"""

"""分治法例子：快速排序"""
"""
快速排序 - 选择枢轴元素进行划分，左边都比枢轴小，右边都比枢轴大
"""
"""
def quick_sort(origin_items,comp=lambda x,y: x <= y):
	items = origin_items[:]
	_quick_sort(items,0,len(items)-1,comp)
	return items

def _quick_sort(items,start,end,comp):
	if start < end:
		pos = _partition(items,start,end,comp)
		_quick_sort(items,start,pos-1,comp)
		_quick_sort(items,pos+1,end,comp)

def _partition(items,start,end,comp):
	pivot = items[end]
	i = start -1
	for j in range(start,end):
		if comp(items[j],pivot):
			i += 1
			items[i],items[j] = items[j],items[i]
	items[i+1],items[end] = items[end],items[i+1]
	return i+1
def main():
	import random
	l = []
	for _ in range(1,1000001):
		l.append(random.randint(1,1000))
	res = quick_sort(l)
	print(res)
main()
"""

"""回溯法：骑士巡逻"""
"""
递归回溯法：叫试探法，按选优条件向前搜索，当搜索到某一步，发现原来选择并不优或达不到
"""
"""
import sys
import time

SIZE = 5
total = 0

def print_board(board):
	for row in board:
		for col in row:
			print(str(col).center(4),end='')
		print()

def patrol(board,row,col,step=1):
	if row >= 0 and row < SIZE and col >= 0 and col < SIZE and board[row][col] == 0:
		board[row][col] = step
		if step == SIZE*SIZE:
			global total
			total += 1
			print(f'第{total}种走法')
			print_board(board)
		patrol(board,row-2,col-2,step+1)
		patrol(board,row-1,col-2,step+1)
		patrol(board,row+1,col-2,step+1)
		patrol(board,row+2,col-1,step+1)
		patrol(board,row+2,col+1,step+1)
		patrol(board,row+1,col+2,step+1)
		patrol(board,row-1,col+2,step+1)
		patrol(board,row-2,col+1,step+1)
		board[row][col] = 0
def main():
	board = [[0] * SIZE for _ in range(SIZE)]
	patrol(board,SIZE-1,SIZE-1)
main()
"""

"""动态规划例子1：斐波拉契数列(不使用动态规划将会是几何级数复杂度)"""
"""
动态规划 - 适用于有重叠子问题和最优子结构性质的问题
使用动态规划方法所耗费时间往往远少于朴素解法(用空间换时间)
"""
"""
def fib(num,temp={}):
	# 用递归计算Fibonacci数
	if num in (1,2):
		return 1
	try:
		return temp[num]
	except KeyError:
		temp[num] = fib(num-1) + fib(num-2)
		return temp[num]
def main():
	print(fib(905))
	print(len(str(fib(750))))
main()
"""

"""动态规划例子2：子列表元素之和的最大值(可使用动态规划避免二重循环)"""
"""
def main():
	items = list(map(int,input().split()))
	size = len(items)
	overall,partial = {},{}
	overall[size-1] = partial[size-1] = items[size-1]
	for i in range(size-2,-1,-1):
		partial[i] = max(items[i],partial[i+1]+items[i])
		overall[i] = max(partial[i],overall[i+1])
	print(overall[0])
main()
"""

# 2、函数的使用方法
"""
1、将函数视为“一等公民”
	函数可以赋值给变量
	函数可以作为函数的参数
	函数可以作为函数的返回值

2、高阶函数的用法(filter,map以及它们的替代品)
"""
"""
filter() 函数用于过滤序列，过滤掉不符合条件的元素，返回由符合条件元素组成的新列表。
该接收两个参数，第一个为函数，第二个为序列，序列的每个元素作为参数传递给函数进行判，
然后返回 True 或 False，最后将返回 True 的元素放到新列表中。
"""
"""
items1 = list(map(lambda x: x ** 2, filter(lambda x:x % 2,range(1,10))))
print(items1)
items2 = [x ** 2 for x in range(1,10) if x % 2] # 奇数%2余1，
print(items2)
"""

"""
3、位置参数、可变参数、关键字参数、命名关键字参数

4、参数的元信息(代码可读性问题)

5、匿名函数和内联函数的用法(lambda函数)

6、闭包和作用域问题
	python中搜索变量的LEGB顺序(Local--> Embeded--> Global--> Built-in)
	global 和 nonlocal 关键字的作用
		global:声明或定义全局变量(要么使用现有的全局作用域的变量，要么定义一个变量放到全局作用域)
		nonllocal：声明使用嵌套作用域的变量(嵌套作用域必须存在该变量，否则报错)

7、装饰器函数(使用装饰器和取消装饰器)
"""
# 例子：输出函数执行时间的装饰器
from functools import wraps
from time import time

def main(num):
	while num:
		print("hello world")
		num = num - 1

def record_time(func):
	"""自定义装饰函数的装饰器"""
	@wraps(func)
	def wrapper(*args,**kwargs):
		start = time()
		result = func(*args,**kwargs)
		print(f'{func.__name__}:{time()-start}秒')
		return result
	return wrapper
record_time(main(1000))
<file_sep># python初始 和 语言元素


"""
Python的优缺点
Python的优点很多，简单的可以总结为以下几点。

简单和明确，做一件事只有一种方法。
学习曲线低，跟其他很多语言相比，Python更容易上手。
开放源代码，拥有强大的社区和生态圈。
解释型语言，天生具有平台可移植性。
支持两种主流的编程范式（面向对象编程和函数式编程）都提供了支持。
可扩展性和可嵌入性，可以调用C/C++代码，也可以在C/C++中调用Python。
代码规范程度高，可读性强，适合有代码洁癖和强迫症的人群。
————————————————————————————————————————————————————————————————————————————
Python的缺点主要集中在以下几点。

执行效率稍低，因此计算密集型任务可以由C/C++编写。
代码无法加密，但是现在的公司很多都不是卖软件而是卖服务，这个问题会被淡化。
在开发时可以选择的框架太多（如Web框架就有100多个），有选择的地方就有错误。

Python的应用领域
目前Python在云基础设施、DevOps、网络爬虫开发、数据分析挖掘、机器学习等领域都有着广泛的应用，
因此也产生了Web后端开发、数据接口开发、自动化运维、自动化测试、科学计算和可视化、
数据分析、量化交易、机器人开发、图像识别和处理等一系列的职位。
"""
'''
import sys

print(sys.version_info)
print(sys.version)
'''



'''
import turtle

turtle.pensize(4)
turtle.pencolor('red')
turtle.forward(100)
turtle.right(90)
turtle.forward(100)
turtle.right(90)
turtle.forward(100)
turtle.right(90)
turtle.forward(100)
turtle.mainloop()
'''
"""——————————————————————————————————————————————————————————————————————————————————————"""

# 指令和程序
"""
计算机的硬件系统通常由五大部件构成，包括：运算器、控制器、存储器、输入设备和输出设备。
其中，运算器和控制器放在一起就是我们通常所说的中央处理器，它的功能是执行各种运算和控制指令以及处理计算机软件中的数据。
“冯·诺依曼结构”有两个关键点，一是指出要将存储设备与中央处理器分开，二是提出了将数据以二进制方式编码。
"""

# 变量和类型
"""
在程序设计中，变量是一种存储数据的载体。计算机中的变量是实际存在的数据或者说是存储器中存储数据的一块内存空间，
变量的值可以被读取和修改，这是所有计算和控制的基础。
计算机能处理的数据有很多种类型，除了数值之外还可以处理文本、图形、音频、视频等各种各样的数据，那么不同的数据就需要定义不同的存储类型

整型：Python中可以处理任意大小的整数（Python 2.x中有int和long两种类型的整数，但这种区分对Python来说意义不大，
因此在Python 3.x中整数只有int这一种了），而且支持二进制（如0b100，换算成十进制是4）、
八进制（如0o100，换算成十进制是64）、十进制（100）和十六进制（0x100，换算成十进制是256）的表示法。

浮点型：浮点数也就是小数，之所以称为浮点数，是因为按照科学记数法表示时，
一个浮点数的小数点位置是可变的，浮点数除了数学写法（如123.456）之外还支持科学计数法（如1.23456e2）。

字符串型：字符串是以单引号或双引号括起来的任意文本，比如'hello'和"hello",
字符串还有原始字符串表示法、字节字符串表示法、Unicode字符串表示法，
而且可以书写成多行的形式（用三个单引号或三个双引号开头，三个单引号或三个双引号结尾）。

布尔型：布尔值只有True、False两种值，要么是True，要么是False，在Python中，可以直接用True、
False表示布尔值（请注意大小写），也可以通过布尔运算计算出来（例如3 < 5会产生布尔值True，而2 == 1会产生布尔值False）。

复数型：形如3+5j，跟数学上的复数表示一样，唯一不同的是虚部的i换成了j
"""

# 变量命名
"""
硬性规则：
变量名由字母（广义的Unicode字符，不包括特殊字符）、数字和下划线构成，数字不能开头。
大小写敏感（大写的a和小写的A是两个不同的变量）。
不要跟关键字（有特殊含义的单词，后面会讲到）和系统保留字（如函数、模块等的名字）冲突。
PEP 8要求：
用小写字母拼写，多个单词用下划线连接。
受保护的实例属性用单个下划线开头（后面会讲到）。
私有的实例属性用两个下划线开头（后面会讲到）。
"""



'''
a = 321
b = 123
print(a + b)
print(a - b)
print(a * b)
print(a / b)
print(a // b)
print(a % b)
print(a ** b)
'''

"""
使用input函数输入
使用int()进行类型转换
用占位符格式化输出的字符串
"""

'''
a = int(input('a = '))
b = int(input('b = '))
print('%d + %d = %d' % (a, b, a + b))
print('%d // %d = %d' % (a, b, a // b))
print('%d ** %d = %d' % (a, b, a ** b))
'''

"""
使用type()检查变量的类型

"""

'''
a = 100
b = 12.345
c = 1 + 5j
d = 'hello world'
e = True

print(type(a))
print(type(int(b))
print(type(c))
print(type(d))
print(type(e))
'''

"""
在对变量类型进行转换时可以使用Python的内置函数
（准确的说下面列出的并不是真正意义上的函数，而是后面我们要讲到的创建对象的构造方法）。

int()：将一个数值或字符串转换成整数，可以指定进制。
float()：将一个字符串转换成浮点数。
str()：将指定的对象转换成字符串形式，可以指定编码。
chr()：将整数转换成该编码对应的字符串（一个字符）。
ord()：将字符串（一个字符）转换成对应的编码（整数）。
"""


'''
a = 5
b = 10
a /= b
print('a = ', a)
'''
'''
flag1 = 3 > 2
flag2 = 2 < 1
flag3 = flag1 and flag2
flag4 = flag1 or flag2
flag5 = not flag1
print("flag1 = ", flag1)
print("flag3 = ", flag3)
print("flag4 = " , flag4)
print("flag5 = ", flag5)
print(flag1 is True)
print(flag2 is not False)
'''

'''
# 华氏温度转为摄氏温度
f = float(input('请输入华氏温度：'))
c = (f-32)/1.8
print('%.1f华氏度 = %.1f摄氏度' %(f, c))
'''

'''
# 输入半径计算园的周长和面积
import math

while True:
	
	radius = float(input('请输入园的半径：'))
	perimetr = 2*math.pi*radius
	area = math.pi*radius*radius
	print('周长：%.2f' % perimetr)
	print('面积：%.2f' % area)
'''

'''
# 输入的年份判断是不是闰年

year = int(input('请输入年份：'))
is_leap = (year%4==0 and year%100!=0 or year%400==0)
print(is_leap)
'''















<file_sep># 进程和线程

"""
今天我们使用的计算机早已进入多CPU或多核时代，而我们使用的操作系统都是支持“多任务”的操作系统，这
使得我们可以同时运行多个程序，也可以将一个程序分解为若干个相对独立的子任务，让多个子任务并发的执
行，从而缩短程序的执行时间，同时也让用户获得更好的体验。因此在当下不管是用什么编程语言进行开发
实现让程序同时执行多个任务也就是常说的“并发编程”，应该是程序员必备技能之一。为此，我们需要先讨论
两个概念，一个叫进程，一个叫线程。

概念
进程就是操作系统中执行的一个程序，操作系统以进程为单位分配存储空间，每个进程都有自己的地址空间、
数据栈以及其他用于跟踪进程执行的辅助数据，操作系统管理所有进程的执行，为它们合理的分配资源。进程
可以通过fork或spawn的方式来创建新的进程来执行其他的任务，不过新的进程也有自己独立的内存空间，因
此必须通过进程间通信机制（IPC，Inter-Process Communication）来实现数据共享，具体的方式包括管道
信号、套接字、共享内存区等。

一个进程还可以拥有多个并发的执行线索，简单的说就是拥有多个可以获得CPU调度的执行单元，这就是所谓
的线程。由于线程在同一个进程下，它们可以共享相同的上下文，因此相对于进程而言，线程间的信息共享和
通信更加容易。当然在单核CPU系统中，真正的并发是不可能的，因为在某个时刻能够获得CPU的只有唯一的
一个线程，多个线程共享了CPU的执行时间。使用多线程实现并发编程为程序带来的好处是不言而喻的，最主
要的体现在提升程序的性能和改善用户体验，今天我们使用的软件几乎都用到了多线程技术，这一点可以利用
系统自带的进程监控工具（如macOS中的“活动监视器”、Windows中的“任务管理器”）来证实，如下图所示。

当然多线程也并不是没有坏处，站在其他进程的角度，多线程的程序对其他程序并不友好，因为它占用了更多
的CPU执行时间，导致其他程序无法获得足够的CPU执行时间；另一方面，站在开发者的角度，编写和调试多
线程的程序都对开发者有较高的要求，对于初学者来说更加困难。

Python既支持多进程又支持多线程，因此使用Python实现并发编程主要有3种方式：多进程、多线程、多进程+多线程。
"""

# python中的多进程
"""
Unix和Linux操作系统上提供了fork()系统调用来创建进程，调用fork()函数的是父进程，创建出的是子
进程，子进程是父进程的一个拷贝，但是子进程拥有自己的PID。fork()函数非常特殊它会返回两次，父进
程中可以通过fork()函数的返回值得到子进程的PID，而子进程中的返回值永远都是0。Python的os模块提
供了fork()函数。由于Windows系统没有fork()调用，因此要实现跨平台的多进程编程，可以使用
multiprocessing模块的Process类来创建子进程，而且该模块还提供了更高级的封装，例如批量启动进程的
进程池（Pool）、用于进程间通信的队列（Queue）和管道（Pipe）等。
"""

"""下面用一个下载文件的例子来说明使用多进程和不使用多进程到底有什么差别，先看看下面的代码。"""
'''
from random import randint
from time import time,sleep

def download_task(filename):
	print('开始下载%s...' % filename)
	time_to_download = randint(5,10)
	sleep(time_to_download)
	print('%s下载完成! 耗费了%d秒！' % (filename, time_to_download))
	
def main():
	start = time()
	download_task('Python从入门到放弃.pdf')
	download_task('Peking Hot.avi')
	end = time()
	print('总共耗费了%.2f秒.' %(end-start))
main()

开始下载Python从入门到放弃.pdf...
Python从入门到放弃.pdf下载完成! 耗费了9秒！
开始下载Peking Hot.avi...
Peking Hot.avi下载完成! 耗费了10秒！
总共耗费了19.03秒
'''
"""
从上面的例子可以看出，如果程序中的代码只能按照顺序一点点往下执行，那么即使执行两个毫不相关的
下载任务，也需要等待一个文件下载完成后才能开始下一个下载任务，很显然着并不合理也没有效率。
接下来我们使用多进程的方式将两个下载任务放到不同的进程中，代码如下所示。
"""
'''
from multiprocessing import Process
from os import getpid
from random import randint
from time import time,sleep

def download_task(filename):
	print('启动下载进程,进程号[%d].' % getpid())
	print('开始下载%s...' % filename)
	time_to_download = randint(5,10)
	sleep(time_to_download)
	print('%s下载完成! 耗费了%d秒' % (filename,  time_to_download))
def main():
	start = time()
	p1 = Process(target=download_task,args=('Python从入门到住院.pdf', ))
	p1.start()
	p2 = Process(target=download_task, args=('Peking Hot.avi', ))
	p2.start()
	p1.join()
	p2.join()
	end = time()
	print('总共耗费了%.2f秒.' % (end-start))
if __name__ == '__main__':
	main()
'''
"""
在上面的代码中，我们通过Process类创建了进程对象，通过 target 参数我们传入一个函数表示
进程启动后要执行的代码，后面的ards是一个元祖，它代表了传递给函数的参数。Process对象的
start 方法用来启动进程，而join 方法表示等待进程执行结束。运行上面的代码可以明显发现两个
下载任务“同时”启动了，而且程序的执行时间将大大缩短，不再是两个任务的时间总和。
下面是程序的一次执行结果。

启动下载进程,进程号[6124].
开始下载Python从入门到住院.pdf...
启动下载进程,进程号[17756].
开始下载Peking Hot.avi...
Peking Hot.avi下载完成! 耗费了5秒
Python从入门到住院.pdf下载完成! 耗费了6秒
总共耗费了6.14秒.

我们也可以使用subprocess模块中的类和函数来创建和启动子进程，然后通过管道和子进程通信，
这些内容不作讲解，有兴趣的读者可以自己了解这些知识。接下来我们将重点放在如何实现两个进程间的通
我们启动两个进程，一个输出Ping，一个输出Pong，两个进程输出的Ping和Pong加起来一共10个。听起
来很简单吧，但是如果这样写可是错的哦。
"""
'''
from multiprocessing import Process
from time import sleep

counter = 0
def sub_task(string):
	global counter
	while counter < 10:
		print(string,end='',flush=True)
		counter += 1
		sleep(0.01)
def main():
	Process(target=sub_task,args=('Ping',)).start()
	Process(target=sub_task,args=('Pong',)).start()
if __name__ == '__main__':
	main()
'''
"""
看起来没毛病，但是最后的的结果是Ping和Pong各输出了10个，Why？当我们在程序中创建进程的时候，子进
程复制了父进程及其所有的数据结构，每个子进程有自己独立的内存空间，这也就意味着两个子进程中各有一
个counter变量，所以结果也就可想而知了。要解决这个问题比较简单的办法是使用multiprocessing模块中
的Queue类，它是可以被多个进程共享的队列，底层是通过管道和信号量（semaphore）机制来实现的，有
兴趣的读者可以自己尝试一下。
"""

# Python中的多线程
"""
在Python早期的版本中就引入了thread模块(现在名为_thread)来实现多线程编程，然而该模块
过于底层，而且很多功能没有提供，因此目前的多线程开推荐使用threading模块，该模块对多线程
编程提供了更好的面向对象的封装。我们把刚才下载文件的例子用多线程的方式来实现一遍。
"""
'''
from random import randint
from threading import Thread
from time import time,sleep

def download(filename):
	print('开始下载%s...' % filename)
	time_to_download = randint(5,10)
	sleep(time_to_download)
	print('%s下载完成! 耗费%d秒' % (filename, time_to_download))
def main():
	start = time()
	t1 = Thread(target=download,args=('Python从入门到放弃.pdf', ))
	t1.start()
	t2 = Thread(target=download,args=('Peking Hot.avi', ))
	t2.start()
	t1.join()
	t2.join()
	end = time()
	print('总共耗耗费了%.3f秒' % (end-start))
if __name__ == '__main__':
	main()
'''
"""
我们可以直接使用threading模块的 Thread 类来创建线程，但是我们之前讲过“继承”，我们可以
从已有的类创建新类，因此也可以通过继承 Thread 类的方式来创建自定义的线程类，然后在创建
线程对象并启动线程。
"""
'''
from random import randint
from threading import Thread
from time import time,sleep

class DownloadTask(Thread):
	def __init__(self, filename):
		super().__init__()
		self._filename = filename
	def run(self):
		print('开始下载%s...' % self._filename)
		time_to_download = randint(5,10)
		sleep(time_to_download)
		print('%s下载完成！耗费了%d秒' % (self._filename, time_to_download))
def main():
	start = time()
	t1 = DownloadTask('Python从入门到住院.pdf')
	t1.start()
	t2 = DownloadTask('Peking Hot.avi')
	t2.start()
	t1.join()
	t2.join()
	end = time()
	print('总共耗费了%.2f秒.' % (end-start))
if __name__ == '__main__':
	main()
'''
"""
因为多个线程可以共享进程的内存空间，因此要实现多个线程间的通信向对简单，大家想到的最直接的
方法就是设置一个全局变量，多个线程共享这个全局变量即可。但是当多个线程共享同一个变量(称之为“资源”)
的时候，很有可能发生不可控制的结果从而导致程序崩溃。如果一个资源被多个线程竞争使用，通常
称之为“临界资源”，对“临界资源”的访问需要加上保护，否则资源会处于“混乱”的状态。
面的例子演示了100个线程向同一个银行账户转账（转入1元钱）的场景，在这个例子中，
银行账户就是一个临界资源，在没有保护的情况下我们很有可能会得到错误的结果。
"""
'''
from time import sleep
from threading import Thread

class Account(object):
	def __init__(self):
		self._balance = 0
	def deposit(self,money):
		# 计算存款后的余额
		new_balance = self._balance + money
		# 模拟受理存款业务需要0.01秒的时间
		sleep(0.01)
		# 修改账户余额
		self._balance = new_balance
	@property
	def balance(self):
		return self._balance
class AddMoneyThread(Thread):
	def __init__(self,account,money):
		super().__init__()
		self._account = account
		self._money = money
	def run(self):
		self._account.deposit(self._money)
def main():
	account = Account()
	threads = []
	# 创建100个存款的线程向同一个账户中存钱
	for _ in range(100):
		t =AddMoneyThread(account, 1)
		threads.append(t)
		t.start()
	# 等所有存款的线程都执行完毕
	for t in threads:
		t.join()
	print('账户余额为￥%d元' % account.balance)
if __name__ == '__main__':
	main()
'''
"""
运行上面的程序，结果让人大跌眼镜，100个线程分别向账户中转入1元钱，结果居然远远小于100元。之所以
出现这种情况是因为我们没有对银行账户这个“临界资源”加以保护，多个线程同时向账户中存钱时，会一起执
行到new_balance = self._balance + money这行代码，多个线程得到的账户余额都是初始状态下的
0，所以都是0上面做了+1的操作，因此得到了错误的结果。在这种情况下，“锁”就可以派上用场了。我们
可以通过“锁”来保护“临界资源”，只有获得“锁”的线程才能访问“临界资源”，而其他没有得到“锁”的线程只能被
阻塞起来，直到获得“锁”的线程释放了“锁”，其他线程才有机会获得“锁”，进而访问被保护的“临界资源”。下面
的代码演示了如何使用“锁”来保护对银行账户的操作，从而获得正确的结果。
"""
'''
from time import sleep
from threading import Thread,Lock

class Account(object):
	def __init__(self):
		self._balance = 0
		self._lock = Lock()
	def deposit(self,money):
		# 先获取锁才能执行后续的代码
		self._lock.acquire()
		try:
			new_balance = self._balance + money
			sleep(0.01)
			self._balance = new_balance
		finally:
			# 在finally中执行释放锁的操作保证正常，异常锁都能释放
			self._lock.release()
	@property
	def balance(self):
		return self._balance
class AddMoneyThread(Thread):
	def __init__(self,account,money):
		super().__init__()
		self._account = account
		self._money = money
	def run(self):
		self._account.deposit(self._money)
def main():
	account = Account()
	threads = []
	for _ in range(100):
		t = AddMoneyThread(account,1)
		threads.append(t)
		t.start()
	for t in threads:
		t.join()
	print('账户余额:￥%d元' % account.balance)
if __name__ == '__main__':
	main()
'''
"""
比较遗憾的一件事情是Python的多线程并不能发挥CPU的多核特性，这一点只要启动几个执行死循环的线程就
可以得到证实了。之所以如此，是因为Python的解释器有一个“全局解释器锁”（GIL）的东西，任何线程执行
前必须先获得GIL锁，然后每执行100条字节码，解释器就自动释放GIL锁，让别的线程有机会执行，这是一个
历史遗留问题，但是即便如此，就如我们之前举的例子，使用多线程在提升执行效率和改善用户体验方面仍然
仍然是有积极意义的。
"""

# 多线程还是多进程
"""
无论是多进程还是多线程，只要数量一多，效率肯定上不去，为什么呢？我们打个比方，假设你不幸正在准备
中考，每天晚上需要做语文、数学、英语、物理、化学这5科的作业，每项作业耗时1小时。如果你先花1小时
做语文作业，做完了，再花1小时做数学作业，这样，依次全部做完，一共花5小时，这种方式称为单任务模
型。如果你打算切换到多任务模型，可以先做1分钟语文，再切换到数学作业，做1分钟，再切换到英语，以此
类推，只要切换速度足够快，这种方式就和单核CPU执行多任务是一样的了，以旁观者的角度来看，你就正在
同时写5科作业。

但是，切换作业是有代价的，比如从语文切到数学，要先收拾桌子上的语文书本、钢笔（这叫保存现场），然
后，打开数学课本、找出圆规直尺（这叫准备新环境），才能开始做数学作业。操作系统在切换进程或者线程
时也是一样的，它需要先保存当前执行的现场环境（CPU寄存器状态、内存页等），然后，把新任务的执行环
境准备好（恢复上次的寄存器状态，切换内存页等），才能开始执行。这个切换过程虽然很快，但是也需要耗
费时间。如果有几千个任务同时进行，操作系统可能就主要忙着切换任务，根本没有多少时间去执行任务了，
这种情况最常见的就是硬盘狂响，点窗口无反应，系统处于假死状态。所以，多任务一旦多到一个限度，反而
会使得系统性能急剧下降，最终导致所有任务都做不好。

是否采用多任务的第二个考虑是任务的类型，可以把任务分为计算密集型和I/O密集型。计算密集型任务的特
是要进行大量的计算，消耗CPU资源，比如对视频进行编码解码或者格式转换等等，这种任务全靠CPU的运
算能力，虽然也可以用多任务完成，但是任务越多，花在任务切换的时间就越多，CPU执行任务的效率就越
低。计算密集型任务由于主要消耗CPU资源，这类任务用Python这样的脚本语言去执行效率通常很低，最能胜
任这类任务的是C语言，我们之前提到了Python中有嵌入C/C++代码的机制。

除了计算密集型任务，其他的涉及到网络、存储介质I/O的任务都可以视为I/O密集型任务，这类任务的特点是
CPU消耗很少，任务的大部分时间都在等待I/O操作完成（因为I/O的速度远远低于CPU和内存的速度）。对于
I/O密集型任务，如果启动多任务，就可以减少I/O等待时间从而让CPU高效率的运转。有一大类的任务都属于
I/O密集型任务，这其中包括了我们很快会涉及到的网络应用和Web应用。
"""

# 单线程+异步I/O
"""
现代操作系统对I/O操作的改进中最为重要的就是支持异步I/O。如果充分利用操作系统提供的异步I/O支持，
就可以用单进程单线程模型来执行多任务，这种全新的模型称为事件驱动模型。Nginx就是支持异步I/O的Web
服务器，它在单核CPU上采用单进程模型就可以高效地支持多任务。在多核CPU上，可以运行多个进程（数量
与CPU核心数相同），充分利用多核CPU。用Node.js开发的服务器端程序也使用了这种工作模式，这也是当下
实现多任务编程的一种趋势。

在Python语言中，单线程+异步I/O的编程模型称为协程，有了协程的支持，就可以基于事件驱动编写高效的
多任务程序。协程最大的优势就是极高的执行效率，因为子程序切换不是线程切换，而是由程序自身控制，因
此，没有线程切换的开销。协程的第二个优势就是不需要多线程的锁机制，因为只有一个线程，也不存在同时
写变量冲突，在协程中控制共享资源不用加锁，只需要判断状态就好了，所以执行效率比多线程高很多。如果
想要充分利用CPU的多核特性，最简单的方法是多进程+协程，既充分利用多核，又充分发挥协程的高效率，
可获得极高的性能。关于这方面的内容，我稍后会做一个专题来进行讲解。
"""

# 应用案例

# 例子1：将耗时间的任务放到线程中以获得更好的体验
"""
如下所示的界面中，有“下载”和“关于”两个按钮，用休眠的方式模拟点击“下载”按钮会联网下载文件需要耗费
10秒的时间，如果不使用“多线程”，我们会发现，当点击“下载”按钮后整个程序的其他部分都被这个耗时间的
任务阻塞而无法执行了，这显然是非常糟糕的用户体验，代码如下所示。
"""
'''
import time
import tkinter
import tkinter.messagebox

def download():
	# 模拟下载任务需要花费10秒的时间
	time.sleep(10)
	tkinter.messagebox.showinfo('提示','下载完成!')
def show_about():
	tkinter.messagebox.showinfo('关于','作者：王朝阳(v1.0)')
def main():
	top = tkinter.Tk()
	top.title('单线程')
	top.geometry('200x150')
	top.wm_attributes('-topmost',True)
	
	panel = tkinter.Frame(top)
	button1 = tkinter.Button(panel,text='下载',command=download)
	button1.pack(side='left')
	button2 = tkinter.Button(panel,text='关于',command=show_about)
	button2.pack(side='right')
	panel.pack(side='bottom')
	
	tkinter.mainloop()
main()
'''
"""
如果使用多线程将耗时间的任务放到一个独立的线程中执行，这样就不会因为执行耗时间的任务而阻塞了主线
程，修改后的代码如下所示。
"""
'''
import time
import tkinter
import tkinter.messagebox
from threading import Thread

def main():
	class DownloadTaskHandler(Thread):
		def run(self):
			time.sleep(10)
			tkinter.messagebox.showinfo('提示','下载完成')
			# 启用下载按钮
			button1.config(state=tkinter.NORMAL)
	def download():
		# 禁用下载按钮
		button1.config(state=tkinter.DISABLED)
		# 通过daemon参数将线程设置为守护线程(主程序退出就不在保留执行)
		# 在线程中处理耗费时间的下载任务
		DownloadTaskHandler(daemon=True).start()
	def show_about():
		tkinter.messagebox.showinfo('关于','作者：王朝阳(v.2.0)')
	top = tkinter.Tk()
	top.title('单线程')
	top.geometry('300x200')
	top.wm_attributes('-topmost',1)
	
	panel = tkinter.Frame(top)
	button1 = tkinter.Button(panel,text='下载',command=download)
	button1.pack(side='left')
	button2 = tkinter.Button(panel,text='关于',command=show_about)
	button2.pack(side='right')
	panel.pack(side='bottom')
	
	tkinter.mainloop()
if __name__ == '__main__':
	main()
'''

# 例子2：使用多进程对复杂任务进行“分而治之”
"""
我们来完成1~100000000求和的计算密集型任务，这个问题本身非常简单，有点循环的知识就能解决，
代码如下所示。
"""
'''
from time import time
def main():
	total = 0
	number_list = (x for x in range(1,100000001))
	start = time()
	for number in number_list:
		total += number
	print(total)
	end = time()
	print('Execution time: %3fs' % (end-start))
if __name__ == '__main__':
	main()	
'''
"""
在上面的代码中，我故意先去创建了一个列表容器然后填入了100000000个数，这一步其实是比较耗时间的，
所以为了公平起见，当我们将这个任务分解到8个进程中去执行的时候，我们暂时也不考虑列表切片操作花费
的时间，只是把做运算和合并运算结果的时间统计出来，代码如下所示。
"""
'''
from multiprocessing import Process,Queue
from random import randint
from time import time

def task_handler(curr_list, result_queue):
	total = 0
	for number in curr_list:
		total += number_class
		result_queue.put(total)
def main():
	processes = []
	number_list1 = (x for x in range(1,100000001))
	number_list = list(number_list1)
	# 这里会出现内存错误，应该是定义的列表过大
	result_queue = Queue()
	index = 0
	# 启动8个线程将数据切片后进行运算
	for _ in range(8):
		p = Process(target=task_handler,args=(number_list[index:index + 12500000],result_queue))
		index += 12500000
		processes.append(p)
		p.start()
	# 开始记录所有进程执行完成花费的时间
	start = time()
	for p in processes:
		p.join()
	# 合并执行结果
	total = 0
	while not result_queue.empty():
		total += result_queue.get()
	print(total)
	end = time()
	print('Execution time:',(end-start),'s',sep='')
if __name__ == '__main__':
	main()
'''

"""
比较两段代码的执行结果（在我目前使用的MacBook上，上面的代码需要大概6秒左右的时间，而下面的代码
只需要不到1秒的时间，再强调一次我们只是比较了运算的时间，不考虑列表创建及切片操作花费的时间），
使用多进程后由于获得了更多的CPU执行时间以及更好的利用了CPU的多核特性，明显的减少了程序的执行时
间，而且计算量越大效果越明显。当然，如果愿意还可以将多个进程部署在不同的计算机上，做成分布式进
程，具体的做法就是通过multiprocessing.managers模块中提供的管理器将Queue对象通过网络共享出来（注
册到网络上让其他计算机可以访问），这部分内容也留到爬虫的专题再进行讲解。
"""





<file_sep># 函数和模块的使用


# 上面的问题等同于将8个苹果分成四组每组至少一个苹果有多少种方案
# 求阶乘
'''
def factorial(num):
	result = 1
	for n in range(1,num + 1):
		result *= n
	return result

m = int(input('m = '))
n = int(input('n = '))

print(factorial(m) // factorial(n) // factorial(m-n))
'''

# 定义函数
"""
在Python中可以使用def关键字来定义函数，和变量一样每个函数也有一个响亮的名字，
而且命名规则跟变量的命名规则是一致的。在函数名后面的圆括号中可以放置传递给
函数的参数，这一点和数学上的函数非常相似，程序中函数的参数就相当于是数学上
说的函数的自变量，而函数执行完成后我们可以通过return关键字来返回一个值，
这相当于数学上说的函数的因变量。
"""

# 函数的参数
"""
在Python中，函数的参数可以有默认值，也支持使用可变参数，
所以Python并不需要像其他语言一样支持函数的重载，因为我们在定义一个函数
的时候可以让它有多种不同的使用方式，下面是两个小例子。
"""

# 摇筛子 param n:色子的个数 return: n颗色子点数之和
'''
from random import randint

def roll_dice(n=2):
	total = 0
	for _ in range(n):
		total += randint(1, 6)
	return total

def add(a=0, b=0, c=0):
	return a + b + c
	
# 如果没有使用参数 使用默认值摇筛子
print(roll_dice())
# 摇三颗
print(roll_dice(3))
print(add())
print(add(1))
print(add(1, 2))
print(add(1, 2, 3))
#传递参数时可以不按照设定的顺序进行传递
print(add(c=50, a=100, b=200))
'''

# 可变参数
# 在参数名前面的*表示args是一个可变参数
# 即在调用add函数时可以传入0个或多个参数
'''
def add(*args):
	total = 0
	for val in args:
		total += val
	return total

print(add())
print(add(1))
print(add(1,2,3,4))
print(add(1,3,5,7,9))
'''

# 用模块管理函数
"""
因为我们会遇到命名冲突这种尴尬的情况。
最简单的场景就是在同一个.py文件中定义了两个同名函数，由于Python没有函数重载的概念，
那么后面的定义会覆盖之前的定义，也就意味着两个函数同名函数实际上只有一个是存在的。

def foo():
    print('hello, world!')


def foo():
    print('goodbye, world!')


# 下面的代码会输出什么呢？
foo()

当然上面的这种情况我们很容易就能避免，但是如果项目是由多人协作进行团队开发的时候，
团队中可能有多个程序员都定义了名为foo的函数，那么怎么解决这种命名冲突呢？
答案其实很简单，Python中每个文件就代表了一个模块（module），我们在不同的模块中可以
有同名的函数，在使用函数的时候我们通过import关键字导入指定的模块就可以区分到底要
使用的是哪个模块中的foo函数，代码如下所示。

module1.py

def foo():
    print('hello, world!')
module2.py

def foo():
    print('goodbye, world!')
test.py

from module1 import foo

# 输出hello, world!
foo()

from module2 import foo

# 输出goodbye, world!
foo()

也可以按照如下所示的方式来区分到底要使用哪一个foo函数。

test.py

import module1 as m1
import module2 as m2

m1.foo()
m2.foo()

但是如果将代码写成了下面的样子，那么程序中调用的是最后导入的那个foo，
因为后导入的foo覆盖了之前导入的foo。

test.py

from module1 import foo
from module2 import foo

# 输出goodbye, world!
foo()

test.py

from module2 import foo
from module1 import foo

# 输出hello, world!
foo()

需要说明的是，如果我们导入的模块除了定义函数之外还中有可以执行代码，
那么Python解释器在导入这个模块时就会执行这些代码，事实上我们可能并不希望如此，
因此如果我们在模块中编写了执行代码，最好是将这些执行代码放入如下所示的条件中
，这样的话除非直接运行该模块，if条件下的这些代码是不会执行的，
因为只有直接执行的模块的名字才是“__main__”。

module3.py

def foo():
    pass


def bar():
    pass


# __name__是Python中一个隐含的变量它代表了模块的名字
# 只有被Python解释器直接执行的模块的名字才是__main__
if __name__ == '__main__':
    print('call foo()')
    foo()
    print('call bar()')
    bar()
test.py

import module3

# 导入module3时 不会执行模块中if条件成立时的代码 因为模块的名字是module3而不是__main__
"""


# 练习
# 实现计算求最大公约数和最小公倍数的函数
'''
def gcd(x, y):
	(x, y) = (y, x) if x > y else (x, y)
	# 从x到0(左闭右开)->[x 0) 每次减1
	for factor in range(x,0,-1):  
		if x % factor == 0 and y %factor == 0:
			return factor

def lcm(x, y):
	return x*y // gcd(x,y)
'''	
# 实现判断一个数是不是回文数的函数
'''
def is_palindrome(num):
	temp = num
	total = 0
	while temp > 0:
		total = total * 10 + temp % 10
		temp //= 10
	return total == num
'''

# 实现判断一个数是不是素数
'''
def is_prime(num):
	for factor in range(2,num):
		if num % factor == 0:
			return False
	return True if num != 1 else False
'''

# 实现一个程序判断输入的正整数是不是回文素数
'''
if __name__ == '__main__':
	num = int(input('请输入整数：'))
	if is_palindrome(num) and is_prime(num):
		print("%d是回文素数" % num)
'''





<file_sep>import socket
import time

# 1 买手机
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 2 拨号
client.connect(('127.0.0.1', 9094))

client.send('hello'.encode('utf-8'))
time.sleep(5)
client.send('world'.encode('utf-8'))

<file_sep># 分支结构



'''
username = input('请输入用户名：')
password = input('请输入口令：')
# 如果希望输入口令时，终端没有回显，可以使用getpass模块的getpass函数
# import getpass
# password = getpass.getpass('请输入口令：')

if username == 'admin' and password == '<PASSWORD>':
	print('身份验证成功！')
else:
	print('身份验证失败！')
'''

'''
import getpass
username = input('请输入用户名：')
password = getpass.getpass('请输入口令：')
if username == 'admin' and password == '<PASSWORD>':
	print('身份验证成功！')
else:
	print('身份验证失败！')
'''

'''
x = float(input('x = '))
if x > 1:
	y = 3 * x - 5
elif x < -1:
	y = x + 2
else:
	y = 5 * x + 3
print('x = %.2f : y =  %.2f' % (x, y))
'''

'''
x = float(input('x = '))
if x >= -1 and x <=1:
	y = x + 2
else:
	if x > 1:
		y = 3 * x - 5
	else:
		5 * x + 3
print('x = %.2f, y = %.2f' %(x, y))
'''

"""
 大家可以自己感受一下这两种写法到底是哪一种更好。
 在之前我们提到的Python之禅中有这么一句话“Flat is better than nested.”，
 之所以提出这个观点是因为嵌套结构的嵌套层次多了之后会严重的影响代码的可读性，
 如果可以使用扁平化的结构就不要去用嵌套，因此之前的写法是更好的做法。
"""

# 练习1：英制单位和公制单位转换
'''
value = float(input('请输入长度：'))
unit = input('请输入单位：')
if unit == 'in' or unit == '英寸':
	print('%f英寸 = %f厘米' %(value, value*2.54))
elif unit == 'cm' or unit == '厘米':
	print('%d厘米 = %f英寸' %(value, value/2.54))
else:
	print('请输入有效的单位')
'''


# 练习2：掷色子决定做什么
'''
from random import randint

face = randint(1,6)
if face == 1:
	result = '唱歌'
elif face == 2:
	result = '跳个舞'
elif face == 3:
	result = '学狗叫'
elif face == 4:
	result = '做俯卧撑'
elif face == 5:
	result = '念绕口令'
else:
	result = '讲笑话'
print(result)
'''

# 练习3：百分制成绩转等级

"""
百分制成绩转等级制成绩
90分以上    --> A
80分~89分    --> B
70分~79分	   --> C
60分~69分    --> D
60分以下    --> E

Version: 0.1
Author: 骆昊
"""

'''
sorce = float(input('请输入成绩：'))
if sorce >= 90:
	grade = 'A'
elif sorce >= 80:
	grade = 'B'
elif sorce >= 70:
	grade = 'C'
elif sorce >= 60:
	grade = 'D'
else:
	grade = 'E'
print('对应的等级是',grade)
'''

# 练习4：输入三条边如果能构成三角形就计算周长和面积
'''
import math

a = float(input('a = '))
b = float(input('b = '))
c = float(input('c = '))
if a + b > c and a + c > b and b + c > a:
	print('周长：%f' %(a+b+c))
	p = (a + b + c)/2
	area = math.sqrt(p*(p-a)*(p-b)*(p-c))
	print('面积：%f' %(area))
else:
	print('不能构成三角形')
'''

# 练习5：个人所得税计算器
'''
salary = float(input('本月收入：'))
insurance = float(input('五险一金：'))
diff = salary - insurance - 3500
if diff <= 0:
	rate = 0
	deduction = 0
elif diff < 1500:
	rate = 0.03
	deduction = 0
elif diff < 4500:
	rate = 0.01
	deduction = 105
elif diff < 9000:
	rate = 0.2
	deduction = 555
elif diff < 35000:
	rate = 0.25
	deduction = 1005
elif deii < 55000:
	rate = 0.3
	deduction = 2755
elif diff < 80000:
	rate = 0.35
	deduction = 5500
else:
	rate = 0.45
	deduction = 13505
tax = abs(diff*rate - deduction)
print('个人所得税：%.2f' % tax)
print('实际到手收入：%.2f' % (diff + 3500-tax))
'''













<file_sep>import struct

'''
res = struct.pack('i', 128000000)
print(res, type(res), len(res))


# conn.recv(4)

obj = struct.unpack('i', res)
print(obj[0])
'''

# res = struct.pack('l', 12800000044443243442432)
# print(res, len(res))
<file_sep>import socket
import struct

# 1 买手机
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 2 拨号
phone.connect(('127.0.0.1', 9094))

# 3 发、收消息
while True:
	# 1 发命令
	cmd = input('>> ').strip() # ls 
	if not cmd:
		continue
	phone.send(cmd.encode('utf-8'))
	
	# 2 拿到命令的结果， 并打印  
	# 第一步：先拿到报头
	header = phone.recv(4)
	total_size = struct.unpack('i', header)[0]
	# 第二步：从报头中解析出对真实数据的描述信息
	
	# 第三步：接收真实的数据
	recv_size = 0
	recv_data=b''
	while recv_size < total_size:
		res= phone.recv(1024)  # 1024是一个坑
		recv_data += res
		recv_size += len(res)
		
	print(recv_data.decode('gbk'))

# 4 关闭
phone.close()
<file_sep>import socket

# 1 买手机
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 2 拨号
phone.connect(('127.0.0.1', 9094))

# 3 发、收消息
while True:
	# 1 发命令
	cmd = input('>> ').strip() # ls 
	if not cmd:
		continue
	phone.send(cmd.encode('utf-8'))
	
	# 2 拿到命令的结果， 并打印  
	data = phone.recv(1024)  # 1024是一个坑
	print(data.decode('gbk'))

# 4 关闭
phone.close()
<file_sep>import socket
import subprocess
import struct
import json

# 1 买手机
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

# 2 插手机卡
phone.bind(('127.0.0.1', 9094)) # bind传入的是一个元祖

# 3 开机
phone.listen(5) # 代表最大挂起的链接数

# 4 等电话
print("start....")

while True: # 链接循环
	conn, client_addr = phone.accept()
	print(client_addr)

	# 5 收发消息

	while True: # 通信循环
		# 客户端单方面断开链接，会导致服务端死循环
		try:
			# 1 接收命令
			cmd = conn.recv(8096) # 1 单位: bytes 2 1024代表最大接收1024个字节
			if not cmd: # 适用于Linux系统
				break
			
			# 2 执行命令， 拿到结果
			obj = subprocess.Popen(cmd.decode('utf-8'), shell=True,
									stdout=subprocess.PIPE, 
									stderr=subprocess.PIPE)
			stdout = obj.stdout.read()
			stderr = obj.stderr.read()
			
			# 3 把命令的结果返回给客户端
			
			# 第一步： 制作固定长度的报头
			header_dict = {
				'filename' : 'a.txt',
				'md5' : 'xxxfxx',
				'total_size' : len(stdout) + len(stderr)
			}
			header_json = json.dumps(header_dict)
			header_bytes = header_json.encode('utf-8')
			
			# 第二步： 先发送报头的长度
			conn.send(struct.pack('i', len(header_bytes)))
			
			# 第三步：再发送报头	
			conn.send(header_bytes) 
			
			# 第四步：在发真实的数据
			conn.send(stdout)
			conn.send(stderr)
		except	ConnectionResetError:  #使用于windows系统
			break
	conn.close()

phone.close()
<file_sep># 循环结构


"""
or-in循环
如果明确的知道循环执行的次数或者是要对一个容器进行迭代，那么我们推荐使用for-in循环，
"""

# 用for循环实现1-100求和

'''
sum = 0
for x in range(101):
	sum += x
print(sum)
# 上面代码中的range类型，range可以用来产生一个不变的数值序列，而且这个序列通常都是用在循环中
# range(101) 可以产生一个0-100的整数序列
# range(1,100)可以产生一个1-99的整数序列
# range(1,100,2)可以产生一个1到99的奇数序列，其中的2是步长，即数值序列的增量
'''

# 用for循环实现1-100之间的偶数求和

'''
sum = 0
for x in range(2,101,2):
	sum += x
print(sum)
'''
'''
sum = 0
for x in range(1,101):
	if x % 2 == 0:
		sum += x
print(sum)
'''

# while 循环

# 猜数字游戏
'''
import random

answer = random.randint(1,101)
counter = 0
while True:
	counter += 1
	number = int(input('请输入：'))
	if number < answer:
		print('大一点')
	elif number > answer:
		print('小一点')
	else:
		print('恭喜你猜对了！')
		break
print('你总共猜了%f' % counter)
if counter > 7:
	print('你的智商余额明显不足')
'''

# 输出乘法口诀表
'''
for i in range(1,10):
	for j in range(1,i+1):
		print('%d*%d=%d' %(i,j,i*j),end = '\t')
	print()
'''

# 练习1：判断一个数是不是素数
'''
from math import sqrt

num = int(input('请输入一个整数：'))
end = int(sqrt(num))
is_prime = True
for x in range(2,end+1):
	if num % x == 0:
		is_prime = False
		break
if is_prime and num != 1:
	print('%d是素数' % num)
else:
	print('%d不是素数' % num)
'''

# 练习2：输入两个整数，计算最大公约数和最小公倍数
'''
x = int(input('x = '))
y = int(input('y = '))
if x > y:
	x,y = y,x
for factor in range(x,0,-1):
	if x % factor == 0 and y % factor == 0:
		print('%d和%d的最大公约数是%d' % (x, y, factor))
		print('%d和%d的最小公倍数是%d' % (x, y, x * y//factor))
		break
'''

# 练习3：打印三角形图案
"""
打印各种三角形图案

*
**
***
****
*****

    *
   **
  ***
 ****
*****

    *
   ***
  *****
 *******
*********

Version: 0.1
Author: 骆昊
"""

'''
row = int(input('请输入行数：'))
for i in range(row):
	for _ in range(i+1):
		print('*',end='')
	print()

for i in range(row):
	for j in range(row):
		if j < row -i - 1:
			print(' ',end='')
		else:
			print('*', end='')
	print()

for i in range(row):
	for _ in range(row - i -1):
		print(' ',end='')
	for _ in range(2* i + 1):
		print('*',end='')
	print()
'''
<file_sep># python中的字符串、列表、元祖、集合、字典

'''
def main():
	# 字符串操作
	str1 = "hello, world!"
	# 通过len函数计算字符串的长度
	print(len(str1))
	# 获取字符串首字母大写的拷贝
	print(str1.capitalize())
	# 获取字符串变大写后的拷贝
	print(str1.upper())
	# 在字符串中查找字串所在的位置
	print(str1.find('or'))
	print(str1.find('shit'))
	# 与find类似但找不到字串时会引发异常
	#print(str1.index('or'))
	#print(str1.index('shit'))
	# 检查字符串是否以指定的字符串开头
	print(str1.startswith('He'))
	print(str1.startswith('hel'))
	# 检查字符串是否以指定字符串结尾
	print(str1.endswith('!'))
	# 将字符串以指定的宽度居中并在两侧填充指定的字符
	print(str1.center(50,' '))
	# 指定字符串以指定的宽度靠右放置左侧填充指定字符
	print(str1.rjust(50,' '))
'''
	
'''
	str2 = 'abc123456'
	# 从字符串中取出指定位置的字符(下标运算)
	print(str2[2])      # c
	# 字符串切片(从指定的开始索引到指定的结束索引)
	print(str2[2:5]) # c12
	print(str2[2:])  # c123456
	print(str2[2::2])  # c246
	print(str2[::2])   # ac246
	print(str2[::-1]) # 654321cba
	print(str2[::-2]) # 642ca
	print(str2[-3:-1]) # 45
	# 检查字符串是否由数字构成
	print(str2.isdigit()) # False
	# 检查字符串是否以字母构成 
	print(str2.isalpha()) # False
	# 检查字符串是否以数字和字母构成
	print(str2.isalnum()) # True
'''

'''
	str3 = '   <EMAIL>'
	print(str3)
	# 获得字符串修建左右两侧空格的拷贝
	print(str3.strip())
'''

'''
	# 列表及其操作
def main():
	list1 = [1, 3, 5 ,7, 100]
	print(list1)
	list2 = ['hello'] * 5
	print(list2)
	# 计算列表长度
	print(len(list1))
	# 下标索引运算
	print(list1[0])
	print(list1[4])
	# print(list1[5]) # IndexError: list index out of range
	print(list1[-1])
	print(list1[-3])
	list1[2] = 300
	print(list1)
	# 添加元素
	list1.append(200)
	print(list1)
	list1.insert(1,400) # 在1的位置插入400，把所有数据向后挪动
	print(list1)
	list1 += [1000, 2000]
	# list1 = [] # python是一个边解释边运行的语言，后面定义的list1会把之前的覆盖，不会像C/C++的语言报错
	print(list1)
	# 删除元素
	list1.remove(3) # 删除是如果元素在列表中不存在，会报异常
	print(list1)
	if 1234 in list1:
		list1.remove(1234)
	print(list1)
	del list1[0]
	print(list1)
	# 清空列表元素
	list1.clear()
	print(list1)
'''

'''
和字符串一样，列表也可以做切片操作，通过切片操作，我们可以实现对列表的复制
或者将列表中的一部分取出来创建出新的列表
'''
'''
def main():
	fruits = ['grape', 'apple', 'strawberry', 'waxberry']
	# print(fruits)
	fruits += ['pitaya', 'pear', 'mango']
	# print(fruits)
	# 循环遍历列表元素
	for fruit in fruits:
		print(fruit.title(), end=' ')
	print()
	# 列表切片
	fruits2 = fruits[1:4]
	print(fruits2)
	fruit3 = fruits # 没有复制列表，只是创建了新的引用
	# 可以通过完整的切片操作来复制列表
	fruits3 = fruits[:]
	print(fruits3)
	fruits4 = fruits[-3:-1]
	print(fruits4)
	# 可以通过反向切片操作来获得翻转后的列表的拷贝
	fruits5 = fruits[::-1]
	print(fruits5)
'''

'''
# 下面的代码实现了对列表的排序操作
def main():
	list1 = ['orange', 'apple', 'zoo', 'pear', 'blueberry']
	list2 = sorted(list1)
	print(list1)
	print(list2)
	# sorted函数返回列表排序后的拷贝不会修改传入的列表
	# 函数的设计就应该像sorted一样尽可能不产生副作用
	list3 = sorted(list1, reverse=True)
	print(list3)
	# 通过key关键字参数指定根据字符串长度进行排序，而不是默认的字母表顺序
	list4 = sorted(list1, key=len)
	print(list4)
	# 给列表对象发出排序消息直接在列表对象上进行排序
	list1.sort(reverse=True)
	print(list1)
'''

# 使用列表的生成语法来创建列表
'''
import sys

def main():
	f = [x for x in range(1,10)]
	print(f)
	f = [x + y for x in 'ABCDE' for y in '123456']
	print(f)
	# 使用列表的生成表达式语法创建列表容器
	# 用这种语法创建列表之后元素已经准备就绪所以需要耗费较多的内存空间
	f = [x ** 2 for x in range(1,1000)]
	print(sys.getsizeof(f)) # 查看对象占用内存的字节数
	print(f)
	# 请注意下面的代码创建的不是一个列表而是一个生成器对象
	# 通过生成器对象可以获取到数据，但它不占用额外的空间存储数据
	# 每次需要数据的时候就通过内部的运算得到数据(需要花额外的时间)
	f = (x ** 2 for x in range(1,1000))
	print(sys.getsizeof(f)) # 相比生成器不占用存储数据的空间
	print(f)
	for val in f:
		print(val,end=' ')
'''

'''
除了上面的生成器语法，python中还有另外一种定义生成器的方式，就是通过yield关键字
将一个普通函数改造成生成器函数。下面的代码演示了如何生成一个斐波那锲数列的生成器。
'''
'''
def fib(n):
	a,b = 0,1
	for _ in range(n):
		a,b = b,a+b
		yield a

def main():
	for val in fib(20):
		print(val)
'''
'''
def main1():
	list1 = ['123',3, True]
	print(list1)
'''

# 元祖
'''
python的元祖与列表类似，不同之处在于元祖的元素不能修改
顾名思义，我们把多个元素组合到一起就形成了一个元祖，所以它和列表一样可以保存多条数据。
'''
'''
def main():
	# 定义元祖
	t = ('王朝阳','23',True,'陕西西安')
	print(t)
	# 获取元祖中的元素
	print(t[0])
	print(t[3])
	# 遍历元祖中的值
	for member in t:
		print(member)
	# 重新给元祖赋值
	# t[0] = '王大锤' # TypeError: 'tuple' object does not support item assignment
	# 变量t重新引用了新的元祖，原来的元祖将被垃圾回收
	t = ('王大锤',20,True, '广州深圳')
	print(t)
	# 将元祖转换成列表
	person = list(t)
	print(person)
	# 列表是可以修改它的元素的
	person[0] = '李小龙'
	person[1] = 25
	print(person)
	# 将列表转化为元祖
	fruits_list = ['apple', 'banana', 'orange']
	fruits_tuple = tuple(fruits_list)
	print(fruits_tuple)
'''

'''
这里有一个非常值得探讨的问题，我们已经有了列表这种数据结构，为什么还需要元祖这种类型呢
1、元祖中的元素是无法修改的，事实上我们在项目中，尤其是多线程环境中可能更喜欢使用的是那些
不变对象(一方面的因为对象状态不能更改，所以可以避免由此引起的不必要的程序错误，简单的
说就是一个不可变的对象要比一个可变的对象更加容易维护；另一方面是因为没有一个线程能够修改
不变对象的内部状态，一个不变对象自动就是线程安全的，这样就可以省掉处理同步化的开销。一个
不变对象可以方便的被共享访问)。所以结论就是：如果不需要对元素进行添加，删除，修改的时候，
可以考虑使用元祖，当然如果一个方法要返回多个值，使用元祖也是不错的选择.
2、元祖在创建时间和占用的空间上面都优于列表。我们可以使用sys模块的getsizeof函数来检查
存储同样的元素的元祖和列表各自占用了多少的内存空间。我们也可以在ipython中使用魔法指令
%timeit来分析创建同样内容的元祖和列表所花费的时间。
'''
'''
def main() :
	import sys
	tuple1 = (1,2,3,4,5,6) # 52
	print(sys.getsizeof(tuple1))	
	list1 = [1,2,3,4,5,6] # 60
	print(sys.getsizeof(list1))
'''

# 集合
# python中的集合跟数学上的集合上一致的，不允许有重复元素，而且可以进行交集、并集、差集的运算
'''
def main():
	# 集合可以类比c++中的set理解，会对插入的数据进行排序+去重
	set1 = {1,2,3,3,3,2}
	print(set1)
	print('lengrh =',len(set1))
	set2 = set(range(1,10))
	print(set2)
	set1.add(4)
	set1.add(5)
	set2.update([11,12])
	print(set1)
	print(set2)
	set2.discard(100) # 删除，没有不会抛异常
	print(set2)
	# remove的元素如果不存在会引发keyError
	if 4 in set2:
		set2.remove(4)
	print(set2)
	# 遍历集合容器
	for elem in set2:
		print(elem ** 2,end=' ')
	print()
	# 将元祖转换成集合
	set3 = set((1,2,3,3,2,1))
	print(set3.pop())
	print(set3)
	'''
	
	'''
	set1 = {1,2,3,3}
	set2 = {1,2,3,4,5}
	# 集合的交集、并集、差集、对称差运算
	print(set1 & set2)
	print(set1 | set2)
	print(set1 - set2)
	print(set1 ^ set2)
	# 判断子集和超集
	print(set1 <= set2)
	print(set1 >= set2)
	print(set3)
	'''
	
# 字典
'''
def main():
	sorces = {'小A':95, '小B':78, '小C':82}
	print(sorces)
	# 通过键可以获取字典中对应的值
	print(sorces['小A'])
	# 对字典进行遍历(遍历的其实是键在通过键取对应的值)
	for elem in sorces:
		print('%s ---> %d' % (elem,sorces[elem]))
	# 更新字典中的元素
	sorces['小B'] = 65
	print(sorces)
	sorces['小D'] = 71
	print(sorces)
	sorces.update(小E=67,小F=85)
	print(sorces)
	if '小G' in sorces:
		print(sorces['小G'])
	print(sorces.get('小G'))
	# get方法也是通过键获取对应的的值，但是可以设置默认值
	print(sorces.get('小G',False))
	# 删除字典中的元素
	print(sorces.popitem())
	print(sorces.popitem())
	print(sorces)
	print(sorces.pop('小A',1000))
	# 清空字典
	sorces.clear()
	print(sorces)
'''

########################################################################

# 练习1：在屏幕上显示跑马灯文字
'''
def main():
	import os
	import time
	
	content = '北京欢迎你，为你开天辟地......'
	while True:
		# 清理出屏幕上输出
		os.system('cls') # os.system('clear')
		print(content)
		# 休眠200毫秒
		time.sleep(0.2)
		content = content[1:] + content[0]
'''

# 练习2：设计一个函数产生制定长度的验证码，验证码由大小写字母和数字构成
'''
def generate_code(code_len=4):
	"""
	生成制定长度的验证码
	:param code_len:验证码的长度(默认四个字符)
	:return:由大小写因为字母和数字构成的随机验证码
	"""
	import random
	
	all_chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
	last_pos = len(all_chars) - 1
	code = ''
	for _ in range(code_len):
		index = random.randint(0,last_pos)
		code += all_chars[index]
	return code
print(generate_code(10))
'''

# 练习3：设计一个函数返回给定文件名的后缀名
'''
def get_suffix(filename, has_dot=False):
	"""
	获取文件名的后缀
	:param filename: 文件名
	:param has_dot: 返回的后缀名是否需要带点
	:return: 文件的后缀名
	"""
	pos = filename.rfind('.')
	if 0 < pos < len(filename) -1:
		index = pos if has_dot else pos + 1;
		return filename[index:]
	else:
		return ''
		
print(get_suffix('简历.pdf',True))
'''

# 练习4：设计一个函数返回传入的列表中最大和第二大的元素
'''
def max2(x):
	m1,m2 = (x[0], x[1]) if x[0] > x[1] else (x[1], x[0])
	for index in range(2, len(x)):
		if x[index] > m1:
			m2 = m1
			m1 = x[index]
		elif x[index] > m2:
			m2 = x[index]
	return m1, m2
print(max2([1,2,3,4,5,6,7,8]))
'''

# 练习5：计算指定的年月日是这一年的第几天
'''
def is_leap_year(year):
	"""
	判断指定年是不是闰年
	：param year: 年份
	:return: 闰年返回True 平年返回False
	"""
	return year%4==0 and year%100!=0 or year%400==0
	
def which_day(year,month,date):
	"""
	计算传入的日期是这一年的第几天
	:param year: 年
	:param month: 月
	:param date: 日
	:return: 第几天
	"""
	days_of_month = [
		[31,28,31,30,31,30,31,31,30,31,30,31],
		[31,29,31,30,31,30,31,31,30,31,30,31]
		][is_leap_year(year)]
	total = 0
	for index in range(month-1):
		total += days_of_month[index]
	return total + date
def main():
	print(which_day(1980,11,29))
	print(which_day(1981,12,31))
	print(which_day(2018,1,1))
	print(which_day(2018,3,1))
main()
'''

# 练习6：打印杨辉三角
'''
def main():
	num = int(input('Number of rows:'))
	yh = [[]] * num
	for row in range(len(yh)):
		yh[row] = [None] * (row + 1)
		for col in range(len(yh[row])):
			if col == 0 or col == row:
				yh[row][col] = 1
			else:
				yh[row][col] = yh[row-1][col] + yh[row-1][col-1]
			print(yh[row][col],end='\t')
		print()
main()
'''

# 综合案例

# 案例1：双色球选号
'''
from  random import randrange,randint,sample
# 使用random模块中的sample函数来实现从列表中选择不重复的N个元素
# enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列，
# 同时列出数据和数据下标，一般用在 for 循环当中。

def display(balls):
	"""
	输出列表中的双色球号码
	"""
	for index,ball in enumerate(balls):
		if index == len(balls) - 1:
			print('|',end=' ')
		print('%-2d' % ball, end=' ')
	print()

def random_select():
	"""
	随机选择一组号码
	"""
	red_balls = [x for x in range(1, 34)]
	selected_balls = []
	selected_balls = sample(red_balls, 6)
	selected_balls.sort()
	selected_balls.append(randint(1, 16))
	return selected_balls

def main():
	n = int(input('机选几注： '))
	for _ in range(n):
		display(random_select())

main()
'''

# 案例二：约瑟夫环问题
"""
《幸运的基督徒》
有15个基督徒和15个非基督徒在海上遇险，为了能让一部分人或下来，不得不将其中15个人扔到
海里面去，有个人想了个办法就是大家围城一圈，由某个人从1报数，报到9的人就扔到海里面，
他后面的人接着从一开始报数，报到9的人继续扔到海里面，直到扔掉15个人。
由于上帝的保佑，15个基督徒都幸免于难，问这些人最开始是怎末站的，那些位置是基督徒，
那些位置是非基督徒。
"""
'''
def main():
	persons = [True] * 30
	counter, index, number = 0, 0, 0
	while counter < 15:
		if persons[index]:
			number += 1
			if number == 9:
				persons[index] = False
				counter += 1
				number = 0
		index += 1
		index %= 30
	tmp = 0
	for person in persons:
		tmp += 1
		print(('%d->基' if person else '%d->非')% tmp,end='  ')

main()
'''

# 案例三：井字棋游戏
'''
import os

def print_board(board):
	print(board['TL'] + '|' + board['TM'] + '|' + board['TR'])
	print('-+-+-')
	print(board['ML'] + '|' + board['MM'] + '|' + board['MR'])
	print('-+-+-')
	print(board['BL'] + '|' + board['BM'] + '|' + board['BR'])

def main():
	init_board = {
		'TL':' ','TM':' ','TR':' ',
		'ML':' ','MM':' ','MR':' ',
		'BL':' ','BM':' ','BR':' '
	}
	begin = True
	while begin:
		curr_board = init_board.copy()
		begin = False
		turn = 'x'
		counter = 0
		os.system('clear')
		print_board(curr_board)
		while counter < 9:
			move = input('轮到%s走棋,请输入位置：' % turn)
			if curr_board[move] == ' ':
				counter += 1
				curr_board[move] = turn
				if turn == 'x':
					turn = 'o'
				else:
					turn = 'x'
			os.system('clear')
			print_board(curr_board)
		choice = input('在玩一局?(yes|no)')
		begin = choice == 'yes'

main()
'''
<file_sep># 总结和练习

# 1、求解100-999之间的所有“水仙花数”。
# 水仙花数是各位立方和等于这个数本身 153 = 1**3 + 5**3 + 3**3
'''
for num in range(100, 1000):
	low = num % 10
	mid = num // 10 % 10
	high = num // 100
	if num == low ** 3 + mid ** 3 + high ** 3:
		print(num)
'''
# 2、找出1-9999之间的所有“完美数”。
# 完美数是除自身外其他所有因子的和正好等于这个数本身的数 6=1+2+3 28=1+2+4+7+14
'''
import time
import math

start = time.clock()
for num in range(1,10000):
	sum = 0
	for factor in range(1, int(math.sqrt(num)) + 1):
		if num % factor == 0:
			sum += factor
			if factor > 1 and num / factor != factor:
				sum += num / factor
	if sum == num:
		print(num)
end = time.clock()
print("执行时间：",(end - start),"秒")
'''
# 3、“百钱百鸡”问题。 
# 1只公鸡5元 1只母鸡3元 3只小鸡1元 用100元买100只鸡 问公鸡 母鸡 小鸡各有多少只
'''
for x in range(0,20):
	for y in range(0,33):
		z = 100 - x - y
		if 5*3 + 3*y +z/3 == 100:
			print('公鸡:%d只, 母鸡:%d只, 小鸡:%d只' % (x, y, z))
'''
# 4、生成“斐波拉切数列”。
'''
a = 0
b = 1
count = 0;
for _ in range(50):
	(a,b) = (b, a + b)
	count += 1
'''

# 5、Craps赌博游戏。
'''
玩家摇两颗筛子 如果第一次摇出7点或11点 玩家胜
如果摇出2点 3点 12点 庄家胜 玩家输
若和为其他点数，则记录第一次的点数和，玩家继续摇筛子，直至点数和等于第一次摇出的点数和则玩家胜
若摇出的点数和为7,则抓庄家胜
玩家进入游戏时有1000元的赌注 全部输光游戏结束
'''
'''
from random import randint

money = 1000
while money > 0:
	print('你的总资产为：', money)
	needs_go_on = False
	while True:
		debt = int(input('请下注：'))
		if debt > 0 and debt <= money:
			break
	first = randint(1, 6) + randint(1,6)
	print('玩家摇出了%d点' % first)
	if first == 7 or first == 1:
		print('玩家胜！')
		money += debt
	elif first == 2 or first == 3 or first == 12:
		print('庄家胜！')
		money -= debt
	else:
		needs_go_on = True
	
	while needs_go_on:
		current = randint(1,6) + randint(1,6)
		print('玩家摇出了%d点' % current)
		if current == 7:
			print('庄家胜！')
			money -= debt
			needs_go_on = False
		elif current == first:
			print('玩家胜！')
			money += debt
			needs_go_on = False
print('你破产了，游戏结束!')
'''
<file_sep>import socket
import struct
import json

# 1 买手机
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 2 拨号
phone.connect(('127.0.0.1', 9094))

# 3 发、收消息
while True:
	# 1 发命令
	cmd = input('>> ').strip() # ls 
	if not cmd:
		continue
	phone.send(cmd.encode('utf-8'))
	
	# 2 拿到命令的结果， 并打印  
	# 第一步：先收报头的长度
	obj = phone.recv(4)
	header_size = struct.unpack('i', obj)[0]
	
	# 第二步： 在收报头
	header_bytes = phone.recv(header_size)
	
	# 第三步：从报头中解析出对真实数据的描述信息
	header_json = header_bytes.decode('utf-8')
	header_dict = json.loads(header_json)
	print(header_dict)
	total_size = header_dict['total_size']
	
	# 第四步：接收真实的数据
	recv_size = 0
	recv_data=b''
	while recv_size < total_size:
		res= phone.recv(1024)  # 1024是一个坑
		recv_data += res
		recv_size += len(res)
		
	print(recv_data.decode('gbk'))

# 4 关闭
phone.close()
<file_sep>import socket
import subprocess
import struct
import json
import os

# 1 买手机
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

# 2 插手机卡
phone.bind(('127.0.0.1', 9094)) # bind传入的是一个元祖

# 3 开机
phone.listen(5) # 代表最大挂起的链接数

# 4 等电话
print("start....")

while True: # 链接循环
	conn, client_addr = phone.accept()
	print(client_addr)

	# 5 收发消息

	while True: # 通信循环
		# 客户端单方面断开链接，会导致服务端死循环
		try:
			# 1 接收命令
			res = conn.recv(8096) # b 'get a.txt'
			if not res: # 适用于Linux系统
				break
			
			# 2 解析命令， 提取响应的参数
			cmds = res.decode('utf-8').split() # ['get', 'a.txt']
			filename = cmds[1]
			
			# 3 以读的方式打开文件， 读取文件内容， 发送给客户端
				
			# 第一步： 制作固定长度的报头
			header_dict = {
				'filename' : r'C:\Users\爱心天使\Desktop\1606990532.jpg',
				'md5' : 'xxxfxx',
				'filesize' : os.path.getsize(filename)
			}
			header_json = json.dumps(header_dict)
			header_bytes = header_json.encode('utf-8')
			
			# 第二步： 先发送报头的长度
			conn.send(struct.pack('i', len(header_bytes)))
			
			# 第三步：再发送报头	
			conn.send(header_bytes) 
			
			# 第四步：在发真实的数据
			with open(filename, 'rb') as f:
				# conn.send(f.read())
				for line in f:
					conn.send(line)
					
		except	ConnectionResetError:  #使用于windows系统
			break
	conn.close()

phone.close()
<file_sep># 图形用户界面和游戏开发
"""
基于tkinter模块的GUI
GUI是图形用户界面的缩写，图形化的用户界面对使用过计算机的人来说应该都不陌生，在此无须进行
赘述.python默认的GUI开发模块是tkinter（在Python 3以前的版本中名为Tkinter），
从这个名字就可以看出它是基于Tk的，Tk是一个工具包，最初是为Tcl设计的，后来被移植到很多
其他的脚本语言中，它提供了跨平台的GUI控件。当然Tk并不是最新和最好的选择，
也没有功能特别强大的GUI控件，事实上，开发GUI应用并不是Python最擅长的工作，
如果真的需要使用Python开发GUI应用，wxPython、PyQt、PyGTK等模块都是不错的选择。

基本上使用tkinter来开发GUI应用需要以下五个步骤:
	1、导入tkinter模块
	2、创建一个顶层窗口对象并用它来承载整个GUI应用
	3、在顶层窗口对象上添加GUI组件
	4、通过代码将这些GUI组件的功能组织起来
	5、进入主事件循环(main loop)
"""
'''
import tkinter
import tkinter.messagebox

def main():
	flag = True
	# 修改标签上的文字
	def change_label_text():
		nonlocal flag
		flag = not flag
		color,msg = ('red','hello world!') if flag else ('blue','goodbye world!')
		label.config(text=msg,fg=color)
	# 确认退出
	def confirm_to_quit():
		if tkinter.messagebox.askokcancel('温馨提示','确定要退出吗?'):
			top.quit()
	# 创建顶层窗口
	top = tkinter.Tk()
	# 设置窗口大小
	top.geometry('240x160')
	# 设置窗口标题
	top.title('小游戏')
	# 创建标签对象并添加到顶层窗口
	label = tkinter.Label(top,text='hello world!',font='Arial -32',fg='red')
	label.pack(expand=1)
	# 创建一个装按钮的容器
	panel = tkinter.Frame(top)
	# 创建按钮对象 指定添加到那个容器 通过command参数绑定事件回调函数
	button1 = tkinter.Button(panel,text='修改',command=change_label_text)
	button1.pack(side='left')
	button2 = tkinter.Button(panel,text='退出',command=confirm_to_quit)
	button2.pack(side='right')
	panel.pack(side='bottom')
	tkinter.mainloop()
main()
'''
"""
需要说明的是，GUI应用通常是事件驱动式的，之所以要进入事件循环就是要监听鼠标，键盘等
各种事件的发生并执行相应的代码事件处理，因为事件会持续发生，所以需要这样的一个循环一直
运行着等待下一个事件的发生。另一方面，Tk为控件的摆放提供了三种布局管理器，通过布局管理器
可以对控件进行定位，这三种布局管理器分别是：Placer(开发者提供控件的大小和摆放位置),
Packer(自动将控件填充到合适的位置) 和 Grid(基于网络坐标来摆放控件)
"""

# 使用pygame进行游戏开发
"""
pygame是一个开源的python模块，专门用于多媒体应用(如电子游戏)的开发，其中包括对图像、
声音、视频、事件、碰撞等的支持。pygame建立在SDL的基础上，SDL是一套跨平台的多媒体开发库
用C语言实现，被广泛的应用于游戏、模拟器、播放器等的开发。而pygame让游戏开发者不再被底层
语言束缚，可以更多的关注游戏的功能和逻辑。

下面我们来完成一个简单的小游戏，游戏的名字叫“大球吃小球”，当然完成这个游戏并不是重点，
学会使用Pygame也不是重点，最重要的我们要在这个过程中体会如何使用前面讲解的
面向对象程序设计，学会用这种编程思想去解决现实中的问题。
"""

# 制作游戏窗口
'''
import pygame
def main():
	# 初始化导入的pygame模块
	pygame.init()
	# 初始化用于显示的窗口并设置窗口尺寸
	screen = pygame.display.set_mode((800,600))
	# 设置当前窗口的标题
	pygame.display.set_caption('大球吃小球')
	running = True
	# 开启一个事件循环处理发生的事件
	while running:
		# 从消息队列中获取事件并对事件进行处理
		for event in pygame.event.get():
			if event.type == pygame.QUIT:
				running = False
'''

# 在窗口中绘图
"""
可以使用pygame中的draw模块的函数在窗口上进行绘图，可以绘制的图形包括:线条、矩形、多边形
园、椭圆、圆弧等。需要说明的是，屏幕坐标系是将屏幕左上角设置为坐标原点(0,0)，向右x轴的
正向，向下是y轴的正向，在表示位置或者设置尺寸时，我们默认的单位都是像素。所谓像素就是屏幕
上的一个点，你可以用浏览器的软件将一张图片放大若干倍，就可以看到这些点。pygame中表示颜色
用的是色光三原色表示法，即通过一个元祖或列表来表示颜色的RGB值，每个值都在0-255之间，因为
是每种原色都用一个8位(bit)的值来表示，三种颜色相当于一共24位构成，这也就是常说的"24位
颜色表示法"
"""
'''
import pygame
def main():
	# 初始化导入的pygame模块
	pygame.init()
	# 初始化用于显示的窗口并设置窗口尺寸
	screen = pygame.display.set_mode((800,600))
	# 设置当前窗口的标题
	pygame.display.set_caption('大球吃小球')
	# 设置窗口的背景色(颜色是由红绿蓝三原色构成的元祖)
	screen.fill((242,242,242))
	# 绘制一个圆(参数分别是：屏幕，颜色，圆心位置，半径，0表示填充圆)
	pygame.draw.circle(screen,(250,0,0),(100,100),30,0)
	# 刷新当前窗口(渲染窗口将绘制的图象展示出来)
	pygame.display.flip()
	running = True
	# 开启一个事件循环处理发生的事件
	while running:
		# 从消息队列中获取事件并对事件进行处理
		for event in pygame.event.get():
			if event.type == pygame.QUIT:
				running = False
main()
'''

# 加载图象
"""
如果需要直接加载图象到窗口上，可以使用pygame中image模块的函数来加载图象，在通过之前的
窗口对象的 blie 方法渲染图象
"""
'''
import pygame
def main():
	# 初始化导入的pygame模块
	pygame.init()
	# 初始化用于显示的窗口并设置窗口尺寸
	screen = pygame.display.set_mode((800,600))
	# 设置当前窗口的标题
	pygame.display.set_caption('大球吃小球')
	# 设置窗口的背景色(颜色是由红绿蓝三原色构成的元祖)
	screen.fill((255,255,255))
	# 通过指定的文件名加载图象
	ball_image = pygame.image.load('./timg.jpg')
	# 在窗口上渲染图象
	screen.blit(ball_image,(50,50))
	# 刷新当前窗口(渲染窗口将绘制的图象展示出来)
	pygame.display.flip()
	running = True
	# 开启一个事件循环处理发生的事件
	while running:
		# 从消息队列中获取事件并对事件进行处理
		for event in pygame.event.get():
			if event.type == pygame.QUIT:
				running = False
main()
'''

# 实现动画效果
"""
说到动画这个词大家都不会陌生事实上要实现动画效果，本身的原理也非常简单，就是将不连续的
图片连续的播放，只要每秒达到了一定的帧数，那么就可以做出比较流畅的动画效果。如果要让上面
代码中的小球动起来，可以将小球的位置用变量来表示，并在循环中修改小球的位置在刷新整个窗口
"""
'''
import pygame
def main():
	# 初始化导入的pygame模块
	pygame.init()
	# 初始化用于显示的窗口并设置窗口尺寸
	screen = pygame.display.set_mode((800,600))
	# 设置当前窗口的标题
	pygame.display.set_caption('大球吃小球')
	# 定义变量来表示小球在屏幕上的位置
	x,y = 50,50
	running = True
	# 开启一个事件循环处理发生的事件
	while running:
		# 从消息队列中获取事件并对事件进行处理
		for event in pygame.event.get():
			if event.type == pygame.QUIT:
				running = False
		screen.fill((255,255,255)) # 设置窗口背景色
		# 绘制一个圆(参数分别是：屏幕，颜色，圆心位置，半径，0表示填充圆)
		pygame.draw.circle(screen,(250,0,0),(x,y),30,0)
		pygame.display.flip() # 刷新当前窗口(渲染窗口将绘制的图象展示出来)
		# 每隔50毫秒就改变小球的位置在刷新窗口
		pygame.time.delay(50)
		x,y = x + 5,y + 5
main()
'''

# 碰撞检测
"""
通常一个游戏中会有很多对对象出现，而这些对象之间的"碰撞"在所难免，比如炮弹击中飞机，箱子
撞到了地面等。碰撞检测在绝大多数的游戏中都是一个必须处理的至关重要的问题，pygame的sprite
(动画精灵)模块就提供了对碰撞检测的支持，这里不做介绍，因为要检测两个小球有没有碰撞只需要
检查球心的距离有没有小于两个球的半径之和。为了制造出更多的小球，我们可以通过对鼠标事件的
处理，在点击鼠标的位置创建颜色，大小和移动速度都随机的小球，当然要做到这一点需要把之前的
面向对象的知识应用起来
"""
'''

from enum import Enum
from math import sqrt
from random import randint
import pygame

# @uinque
class Color(Enum):
	"""颜色"""
	RED = (250,0,0)
	GREEN = (0,250,0)
	BLUE = (0,0,250)
	BLACK = (0,0,0)
	WHITE = (255,255,255)
	GRAY = (242,242,242)
	@staticmethod
	def random_color():
		"""获得随机颜色"""
		r = randint(0,255)
		g = randint(0,255)
		b = randint(0,255)
		return (r,g,b)
class Ball(object):
	"""球"""
	def __init__(self,x,y,radius,sx,sy,color=Color.RED):
		"""初始化方法"""
		self.x = x
		self.y = y
		self.radius = radius # 球的半径
		self.sx = sx
		self.sy = sy
		self.color = color
		self.alive = True
	def move(self,screen):
		"""移动"""
		self.x += self.sx
		self.y += self.sy
		if self.x - self.radius <= 0 or self.x - self.radius >= screen.get_width():
			self.sx = -self.sx
		if self.y - self.radius <= 0 or self.y + self.radius >= screen.get_height():
			self.sy = -self.sy
	def eat(self,other):
		"""吃其他球"""
		if self.alive and other.alive and self != other:
			dx,dy = self.x - other.x,self.y - other.y
			distance = sqrt(dx**2 + dy**2)
			if distance < self.radius + other.radius and self.radius > other.radius:
				other.alive = False
				self.radius = self.radius + int(other.radius*0.146)
	def draw(self,screen):
		"""在窗口上绘制球"""
		pygame.draw.circle(screen,self.color,(self.x,self.y),self.radius,0)


# 事件处理
"""
可以在事件循环中对鼠标事件进行处理，通过事件对象的 type 属性可以判定事件类型，在通过
pos 属性就可以获得鼠标点击的位置。如果要处理键盘事件也是在这个地方，做法和处理鼠标事件
类似
"""
def main():
	# 定义用来装所有球的容器
	balls = []
	# 初始化导入的pygam模块
	pygame.init()
	# 初始化用于显示的窗口并设置窗口尺寸
	screen = pygame.display.set_mode((800,600))
	# 设置当前窗口的标题
	pygame.display.set_caption('大球吃小球')
	running = True
	# 开启一个事件循环处理发生的事件
	while running:
		# 从消息队列中获取事件并对事件进行处理
		for event in pygame.event.get():
			if event.type == pygame.QUIT:
				running = False
			# 处理鼠标事件的代码
			if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
				# 获得点击鼠标的位置
				x,y = event.pos
				radius = randint(10,100)
				sx,sy = randint(-10,10),randint(-10,10)
				color = Color.random_color()
				# 在点击鼠标的位置创建一个球(大小，速度和颜色随机)
				ball = Ball(x,y,radius,sx,sy,color)
				# 将球添加到列表容器中
				balls.append(ball)
		screen.fill((255,255,255))
		# 取出容器中的球 如果没被吃掉就绘制 被吃掉了就移除
		for ball in balls:
			if ball.alive:
				ball.draw(screen)
			else:
				balls.remove(ball)
		pygame.display.flip() # 刷新当前窗口(渲染窗口将绘制的图象展示出来)
		# 每隔50毫秒就改变球的位置在刷新窗口
		pygame.time.delay(50)
		for ball in balls:
			ball.move(screen)
			# 检查有没有吃到其他的球
			for other in balls:
				ball.eat(other)
main()
'''
			




<file_sep>import socket
import struct
import json

# 1 买手机
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 2 拨号
phone.connect(('127.0.0.1', 9094))

# 3 发、收消息
while True:
	# 1 发命令
	cmd = input('>> ').strip() # get a.txt
	if not cmd:
		continue
	phone.send(cmd.encode('utf-8'))
	
	# 2 接收文件的内容，以写的方式打开一个新文件，接收服务端发来的文件内容，写入客户端的新文件
	
	# 第一步：先收报头的长度
	obj = phone.recv(4)
	header_size = struct.unpack('i', obj)[0]
	
	# 第二步： 在收报头
	header_bytes = phone.recv(header_size)
	
	# 第三步：从报头中解析出对真实数据的描述信息
	header_json = header_bytes.decode('utf-8')
	header_dict = json.loads(header_json)
	print(header_dict)
	total_size = header_dict['filesize']
	filename = header_dict['filename']
	
	# 第四步：接收真实的数据
	with open(filename, 'wb') as f:
		recv_size = 0
		while recv_size < total_size:
			line= phone.recv(1024)  # 1024是一个坑
			f.write(line)
			recv_size += len(line)

# 4 关闭
phone.close()
<file_sep>import socket
import time

# 1 买手机
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

# 2 插手机卡
server.bind(('127.0.0.1', 9094)) # bind传入的是一个元祖

# 3 开机
server.listen(5) # 代表最大挂起的链接数

conn, addr = server.accept()

res1 = conn.recv(1)
print('第一次', res1)

time.sleep(6)
res2 = conn.recv(1024)
print('第二次', res2)
<file_sep>import socket

# 1 买手机
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

# 2 插手机卡
phone.bind(('127.0.0.1', 9094)) # bind传入的是一个元祖

# 3 开机
phone.listen(5) # 代表最大挂起的链接数

# 4 等电话
print("start....")
conn, client_addr = phone.accept()
print(client_addr)

# 5 收发消息
while True: # 通信循环
	# 客户端单方面断开链接，会导致服务端死循环
	try:
		data = conn.recv(1024) # 1 单位: bytes 2 1024代表最大接收1024个字节
		if not data: # 适用于Linux系统
			break
		print('客户端数据', data)

		conn.send(data.upper
	except ConnectionResetError: # 使用于windows系统
		break

conn.close()

phone.close()
<file_sep>import struct
import json


header_dict = {
	'filename' : 'a.txt',
	'md5' : 'xxxfxx',
	'total_size' : 33333333333333333333333333333333333333
}
header_json = json.dumps(header_dict)
print(header_json, type(header_json))
header_bytes = header_json.encode('utf-8')
# print(header_bytes)
print(len(header_bytes))

struct.pack('i', len(header_bytes))
<file_sep># 文件和异常
"""
在实际开发中，常常需要对程序中的数据进行持久化操作，而实现数据持久化最简单的方式就是将
数据保存到文件中。说到"文件"这个词，需要先了解一下文件系统，这个自行了解

在python中实现文件的读写操作非常简单，通过python内置的open函数，我们可以指定文件名，
操作模式，编码信息等来获得操作文件的对象，接下来就可以对文件进行读写操作了。这里的操作
模式指要打开的文件(字符文件还是二进制文件)以及做什么样的操作(读，写还是追加)。

操作模式	具体含义
'r'	读取 （默认）
'w'	写入（会先截断之前的内容）
'x'	写入，如果文件已经存在会产生异常
'a'	追加，将内容写入到已有文件的末尾
'b'	二进制模式
't'	文本模式（默认）
'+'	更新（既可以读又可以写）
"""

# 读文本文件
"""
读取文本文件时，需要在使用open函数时指定好带路劲的文件名(可以使用相对路径或绝对路径)
并将文件模式设置为"r"(如果不指定，默认值是"r"),然后通过 encoding 参数指定编码(如果
不指定，默认是None，那么在读取文件时使用的是操作系统默认的编码)，如果不能保证文件使用的
编码方式与encoding参数指定的编码方式一致，那么就可能无法解码字符而导致读取失败。
下面的例子演示如何读取一个纯文本文件
"""
'''
def main():
	f = open('致橡树.txt', 'r', encoding='utf-8')
	print(f.read())
	f.close()
main()
'''

""""
请注意上面的代码，如果open函数指定的文件并不存在或者无法打开，那么将引发异常状况导致
程序崩溃。为了让代码有一定的健壮性和容错性，我们可以使用Python的异常机制
对可能在运行时发生状况的代码进行适当的处理，如下所示。
"""
'''
def main():
	f = None
	try:
		f= open('致橡树.txt', 'r', encoding='utf-8')
		print(f.read())
	except FileNotFoundError:
		print('无法打开指定文件')
	except LookupError:
		print('指定了位置的编码')
	except UnicodeDecodeError:
		print('读取文件时解码错误')
	finally:
		if f:
			f.close()
main()
'''
"""
在Python中，我们可以将那些在运行时可能会出现状况的代码放在try代码块中，在try代码块的后面可以跟上
个或多个except来捕获可能出现的异常状况。例如在上面读取文件的过程中，文件找不到会引发
FileNotFoundError，指定了未知的编码会引发LookupError，而如果读取文件时无法按指定方式解码会引发
UnicodeDecodeError，我们在try后面跟上了三个except分别处理这三种不同的异常状况。
最后我们使用finally代码块来关闭打开的文件，释放掉程序中获取的外部资源，由于finally块的代码不论程序正常还是异
常都会执行到（甚至是调用了sys模块的exit函数退出Python环境，finally块都会被执行，因为exit函数
实质上是引发了SystemExit异常），因此我们通常把finally块称为“总是执行代码块”，它最适合用来做释放外
部资源的操作。如果不愿意在finally代码块中关闭文件对象释放资源，也可以使用上下文语法，通过with关键
字指定文件对象的上下文环境并在离开上下文环境时自动释放文件资源，代码如下所示
"""
'''
def main():
	try:
		with open('致橡树.txt','r', encoding='utf-8') as f:
			print(f.read())
	except FileNotFoundError:
			print('无法打开指定文件')
	except LookupError:
			print('指定了未知的编码')
	except UnicodeDecodeError:
			print('读取文件时解码错误')
main()
'''
"""
除了使用文件对象的read方法读取文件之外，还可以使用for-in循环逐行读取或者使用readlines方法
将文件按行读取到一个列表容器中。
"""
'''
import time
def main():
	# 一次性读取整个文件内容
	with open('致橡树.txt','r',encoding='utf-8') as f:
		print(f.read())
	# 通过for-in循环逐行读取
	with open('致橡树.txt',mode='r',encoding='utf-8') as f:
		for line in f:
			print(line,end='')
			time.sleep(0.5)
	print()
	# 读取文件按行读取到列表中
	with open('致橡树.txt',encoding='utf-8') as f:
		lines = f.readlines()
	print(lines)

main()
'''

"""
要将文本信息写入文件也非常简单，在使用open函数时指定好文件名字并将文件模式设置为'w',
注意需要对文件内容进行追加写入，应该讲模式设置为'a'。如果要写入的文件不存在会自动创建
文件而不是引发一场。
下面的例子演示了如何将1-9999之间的素数分别写入三个文件中（1-99之间的素数保存在a.txt中，
100-999之间的素数保存在b.txt中，1000-9999之间的素数保存在c.txt中）。
"""
'''
from math import sqrt
def is_prime(n):
	"""判断素数的函数"""
	assert n > 0
	for factor in range(2,int(sqrt(n))+1):
		if n % factor == 0:
			return False
	return True if n != 1 else False
def main():
	filenames = ('a.txt','b.txt','c.txt')
	fs_list = []
	try:
		for filename in filenames:
			fs_list.append(open(filename,'w',encoding='utf-8'))
		for number in range(1,10000):
			if is_prime(number):
				if number < 100:
					fs_list[0].write(str(number)+'\n')
				elif number < 1000:
					fs_list[1].write(str(number)+'\n')
				else:
					fs_list[2].write(str(number)+'\n')
	except IOError as ex:
		print(ex)
		print('写文件时发生错误')
	finally:
		for fs in fs_list:
			fs.close()
	print('操作完成')
main()
'''

# 读写二进制文件
"""知道了如何读写文本文件要读写二进制文件也就很简单了，下面的代码实现了复制图片文件的功能"""
'''
def main():
	try:
		with open('timg.jpg','rb') as fs1:
			data = fs1.read()  # <class 'bytes'>
			print(type(data))
		with open('皮卡丘jpg','wb') as fs2:
			fs2.write(data)
	except FileNotFoundError as e:
		print('指定的文件无法打开')
	except IOError as e:
		print('读写文件时出现错误')
	print('程序结束')
main()
'''

# 读取json文件
"""
如果希望吧一个列表或者一个字典中的数据保存到文件中该怎么做呢？答案是将数据以json格式保存，
JSON是“JavaScript Object Notation”的缩写，它本来是JavaScript语言中创建对象的一种字面量语法，
现在已经被广泛的应用于跨平台跨语言的数据交换，原因很简单，因为JSON也是纯文本，任何系统任何编程语
言处理纯文本都是没有问题的。目前JSON基本上已经取代了XML作为异构系统间交换数据的事实标准。
关于JSON的知识，更多的可以参考JSON的官方网站，从这个网站也可以了解到每种语言处理JSON数据格式
可以使用的工具或三方库，下面是一个JSON的简单例子。
"""
'''
{
	"name":"爱心天使",
	"age":23,
	"qq":123456,
	"friends":["蔡文姬","曹操"],
	"cars":[
		{"brand":"BYD","max_speed":1800},
		{"brand":"Audi","max_speed":280},
		{"brand":"Benz","max_speed":320}
	]
}
可能大家已经注意到了，上面的JSON跟Python中的字典其实是一样一样的，
事实上JSON的数据类型和Python的数据类型是很容易找到对应关系的，如下面两张表所示。
'''
"""
JSON			Python
---------------------------------------------------------
object			dict
array	        list
string			str
number 			(int / real)	int / float
true / false	True / False
null			None
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Python										JSON
----------------------------------------------------------
dict										object
list, tuple									array
str											string
int, float, int- & float-derived Enums		number
True / False								true / false
None										null
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
"""

"""我们可以使用Python中的json模块就可以将字典或列表以json格式保存到文件中"""
'''
import json

def main():
	mydict = {
		"name":"爱心天使",
		"age":23,
		"qq":123456,
		"friends":["蔡文姬","曹操"],
		"cars":[
			{"brand":"BYD","max_speed":1800},
			{"brand":"Audi","max_speed":280},
			{"brand":"Benz","max_speed":320}
		]
	}
	try:
		with open('data.json','w',encoding='utf-8') as fs:
			json.dump(mydict,f)
	except IOError as e:
		print(e)
	print("数据保存完成")
main()
'''
"""
json 模块有四个比较重要的函数，分别是：
	dump-将python对象按照json格式序列化到文件中
	dumps-将python对象处理成json格式的字符串
	load-将文件中的json数据反序列化成对象
	loads-将字符串的内容反序列化成python对象
"""
"""
这里出现了两个概念，一个叫序列化，一个叫反序列化。自由的百科全书维基百科上对这两个概念是这样解
释的：“序列化（serialization）在计算机科学的数据处理中，是指将数据结构或对象状态转换为可以存储或
传输的形式，这样在需要的时候能够恢复到原先的状态，而且通过序列化的数据重新获取字节时，可以利用
这些字节来产生原始对象的副本（拷贝）。与这个过程相反的动作，即从一系列字节中提取数据结构的操
作，就是反序列化（deserialization）”。

目前绝大多数网络数据服务（或称之为网络API）都是基于HTTP协议提供JSON格式的数据，关于HTTP协议
的相关知识，可以看看阮一峰老师的《HTTP协议入门》，如果想了解国内的网络数据服务，可以看看聚合
数据和阿凡达数据等网站，国外的可以看看{API}Search网站。下面的例子演示了如何使用requests模块（封
三方网络访问模块）访问网络API获取国内新闻，如何通过json模块解析JSON数据并显示新闻标题，这个例子使
用了天行数据提供的国内新闻数据接口，其中的APIKey需要自己到该网站申请
"""
'''
——————————————————————————————————————————————————————————————————————————————————————————————————————
import requests
import json
def main():
	resp = requests.get('http://api.tianapi.com/guonei/?key=<KEY>&num=11')
	data_model = json.loads(resp.text)
	for news in data_model['newslist']:
		print(news['title'])
main()
'''
'''
另外，如果要了解更多的关于Python异常机制的知识，可以看看segmentfault上面的文章《总结：Python中的异常处理》
，这篇文章不仅介绍了Python中异常机制的使用，还总结了一系列的最佳实践，很值得一读。
链接：https://segmentfault.com/a/1190000007736783
'''
<file_sep>import sys

import pygame

from settings import Settings
from ship import Ship
import game_functions as gf
from pygame.sprite import Group
from alien import Alien
from game_stats import GameStats
from button import Button

def run_game():
	#初始化游戏并创建一个屏幕对象
	pygame.init()
	ai_settings = Settings()
	
	screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
	pygame.display.set_caption("Alien Invansion")
	
	#创建一个用于存储游戏统计信息的实例
	stats = GameStats(ai_settings)
	
	#创建一艘飞船,一个子弹编组和一个外星人编组
	ship = Ship(ai_settings, screen)
	bullets = Group()
	aliens = Group()
	
	#创建外星人群 
	gf.creat_fleet(ai_settings, screen, ship, aliens)
	
	#创建play按钮
	#play_button = Button(ai_settings, screen, "Play")
	
	#开始游戏主循环
	while True:
		gf.check_events(ai_settings, screen, ship,bullets)
		
		if stats.game_active:
			ship.update()
			gf.update_bullets(ai_settings, screen,ship, aliens, bullets)
			gf.update_aliens(ai_settings,stats,screen, ship, aliens, bullets)
			
		gf.update_screen(ai_settings, screen,stats,  ship,aliens, bullets)

run_game()
	
