精华内容
下载资源
问答
  • 使用uniapp+uview+tailwindcss开发的一个小程序
  • 基于班课APP在中职物理课堂移动评价的研究.pdf
  • 网易音乐APP分析

    千次阅读 2017-10-07 17:20:00
    之前用的一直是QQ音乐,但是有一天一个朋友分享了一首网易上的音乐(顺便分享一下歌名:Drop By Drop),觉得挺好听的,本来想用QQ音乐下载,却发现找不到这一首歌,于是便下载了网易音乐,发现还挺好用的,有些...

    网易云音乐-感受音乐的力量

     


     

    • 你选择的产品是?

               网易云音乐

     

     

    • 为什么选择该产品作为分析?

              之前用的一直是QQ音乐,但是有一天一个朋友分享了一首网易云上的音乐(顺便分享一下歌名:Drop By Drop),觉得挺好听的,本来想用QQ音乐下载,却发现找不到这一首歌,于是便下载了网易云音乐,发现还挺好用的,有些歌曲也不像QQ音乐那样需要收费而是可以免费下载。

     

     

    • 该产品是怎么诞生的(在什么样的背景下)?

    背景[1]

         在新旧技术标准的竞争中,数字时代的音乐产业实现了企业战略、技术、产业组织与制度的共同演化,产业内新的利益均衡逐渐实现,“产权”边界重新得到了界定,新的产业格局与秩序逐步形成,新的产业组织模式与企业盈利模式逐步浮现诞生。而如今,一般的数字音乐技术也已经过时,流媒体技术的革新发展助推整个无线音乐市场飞速发展。无线音乐市场广阔,作为一款娱乐增值型消费产品,无线音乐属于价格敏感型产品。无线音乐的使用者对于目前的无线音乐服务的满意度基本处于整体偏上的水准,满意的比例占到一半略多一点。相对而言,无线音乐市场还拥有很大的进步空间。

         目前,市场上针对于无线音乐提供运营服务的公司主要有QQ音乐、酷狗音乐、酷我 音乐、虾米音乐、豆瓣音乐、百度音乐、多米音乐、音悦台等。他们的主要消费群体都是集中在手机客户端,而据速途研究院的《2015年移动音乐市场报告》显示,习惯在移动端听歌的用户占到了56.6%,而在PC端听歌的用户仅占到22.4%,还有17.3%的用户在移动端和PC端使用参半,另有3.7%的用户表示不经常使用音乐产品。

     

    (图片来自速途研究院)

    诞生:

         网易云音乐是由网易杭州研究院20127月开始研发,由前网易娱乐频道主编王磊担任项目总监。 2013124日晚间,网易低调在iTunes商店上线其在移动端的第一款音乐应用“网易云音乐”,并标榜是一款“依托专业音乐人、DJ、好友推荐及社交功能,专注于发现与分享的音乐产品”。三个月后,丁磊带着快速迭代出来的 iOS V1.3Android V1.2版本网易云音乐客户端在北京高调举行发布会,宣布网易正式进军数字音乐领域。在发布会上,丁磊声称基于PC时代音乐产品和模式将会过时。“移动化、云服务、社会化三大特征让数字音乐的传播和消费发生了革命性的变化,智能手机会成为获取音乐的最主要方式。移动互联时代的社交属性与音乐的社交属性也实现合流,无互动,不音乐”。

     

     

    • 经历了哪些发展阶段?(软件得到发展或者没落的原因是什么,比如微信的兴起给微博带来的打击)

    发展历程[2]

    2012.7           由网易杭州研究院开始研发网易云音乐。

    2013.1.24      网易低调在iTunes商店上线其在移动端的第一款音乐应用“网易云音乐”。

    2013.03.12    网易云音乐iOS1.1.0上线

    2013.04.23    网易云音乐移动版本正式发布

    2013.4           网易云音乐累计用户超过2000万,入驻的明星超过500位,收录音乐越500万首,用户自主创建的歌单3200万个。

    2013.05.21    网易云音乐android 1.1.1上线(音乐指纹、社交、大牌DJ功能上线)

    2013.05.27    网易云音乐android 1.2.1上线

    2013.07.04    网易云音乐android 1.3.1上线

    2013.07.31    网易云音乐android 1.3.2上线

    2013.08.26    网易云音乐android 1.4.0上线

    2013.09.11    网易云音乐android 1.4.1上线

    2013.09.17    网易云音乐android 1.4.2上线

    2013.9           网易云音乐上线八个月的时间,注册用户已达600万。

    2014.01.02    网易云音乐android 1.4.3上线

    2014.02.12    网易云音乐android 1.6.1上线

    2014.3.25      网易云音乐召开发布会,发布了《2013-2014年度网易云音乐白皮书》,同时也正式启动校园音乐开放日活动。

    2014.5.27      公布了这一年多以来,网易云音乐取得的成绩:拥有3200万个用户自主创建歌单,成为中国最大的在线歌单音乐库。

    2014.08.29    网易云音乐android 1.7.1上线

    2014.8.30      网易云音乐正式上线Windows Phone平台,至此实现iPhoneAndroidWebPCiPadWPMac七大平台客户端全覆盖。

    2014.01-09    网易云音乐以36.5%的用户月增长率居音乐类App的第一位

    2014.12.24    网易云音乐android 2.0.2上线(界面换新,个性化推荐、朋友动态)

    2015.01.16    网易云音乐android 2.3.3上线(主题换肤)

    2015.01.20    网易云音乐android 2.4.0上线

    2015.02.05    网易云音乐android 2.4.1上线

    2015.02.11    网易云音乐android 2.5.1上线

    2015.03.23    网易云音乐android 2.5.4上线

    2015.7.14      网易云音乐宣布其用户数突破1亿,累计产生1.2亿条乐评和2亿次的音乐分享。用户自主创建的歌单超过 6400万个。

    2015.10.13   QQ音乐与网易云音乐达成版权合作,向网易云音乐转授音乐版权150万首。

    得到发展的原因[3]

         相较于竞争对手,网易云音乐虽然起步很晚,远落后于其他公司,况且还面对极大风险,但是网易云音乐依靠准确的定位,先在用户群中建立移动音乐社区转而以用户为中心打造音乐生态圈。网络音乐的市场份额每年都在增长,这个是毋庸置疑的,一面是传统唱片公司的萎靡,一面是网络用户的增长,两方面都驱使网络音乐整体的市场份额快速增长。移动互联网的崛起在近5年的影响格外明显,互联网的发展壮大冲击着各个传统互联网领域,新闻门户、社交网络、电商等莫不如是,移动智能设备近年来的快速发展也为无线音乐市场的增长孕育了温床,网易云音乐在此时进入市场,也可以说是搭上了移动音乐的末班车。网易云音乐注重消费者体验,在应用的UI设计、用户反馈等各方面都加以用心,甚至在推荐方面都采取人工推荐与机器推荐相结合的方式结合,对于一款音乐产品真诚的用心和独树一帜的姿态令人折服。有了移动基因的先天优势之后,网易云音乐对于产品的定位有了更深层面的理解。网易云音乐允许用户导入原有手机通讯、SNS 关系,还能用 LBS 查看附件用户分享的好音乐。这等等一系列的动作,都在告诉用户,这是一个音乐社区应用,而不仅仅是一款音乐播放器。

     


     

    1.1 调研评测

    (1)下载软件并使用起来,描述最简单直观的个人第一次上手体验。

          打开APP首先进入的是网易云音乐用户界面,类似于QQ音乐的“我的”界面,但是网易云音乐的界面相对会简约一点,看起来比较舒适,因为QQ音乐使用QQ账号进行登录以后经常会出现一些推广产品,导致页面的美观度有所下降。切换到网易云音乐的主页面后,可以选择推荐歌单、最新音乐等等,并可以观看音乐视频以及可以收听电台等功能,在发现页面可以查看别人分享的音乐视频,总的来说算是面面俱到了,可以满足听歌的需求。

    2)选择一个朋友(用户)进行采访,并加以记载。

    • 介绍采访对象的背景和需求。

              背景:在校大学生,计算机专业,没有使用过这个APP,一般使用QQ音乐

              需求:基本的歌曲搜索,听歌以及观看音乐视频

    • 让采访对象使用该产品的功能。

              

    • 描述用户使用这个产品的过程,用户的问题解决了么?软件在数据量/界面/功能/准确度上各有什么优缺点?用户体验方面有问题么?

             用户的需求问题网易云音乐基本可以满足,在数量上,常见的歌曲基本可以搜索到并且能够下载,界面也比较美观,上手简单。功能上,网易云音乐提供了歌曲搜索、分享、音乐视频的观看等等比较实用的功能。在准确度上,用户可以通过简拼搜索歌曲,效果蛮准确的。用户体验方面,由于之前没有使用过网易云音乐并且试用的时间也不长,因此没有遇到较大的问题,总体上满意度不错。

    • 用户对产品有什么改进意见?
    1. 在网易云音乐网络不可用时,进行刷新时默认显示当前网络不可用,这是一个很好的反馈,但是在没有网络时只能听已下载的歌曲,对于收藏的歌曲并没有进行缓存。
    2. 在目前最新版本里,沉浸式之后顶部空间因为少了色块之间的强弱关系对比,缺少视觉强调,使得顶部范围有时看起来很乱。
    3. 靠广告维持运营支出等费用无可厚非,但因为这样可能会导致一部分用户的抵制,造成一定的用户流失。
    4. 播放页面的胶片旋转虽然好看,但是有些时候想看专辑的时候就很不方便了,建议可以让我们直接点击一下专辑,弹出专辑图片。

    3)结论:经过这么多工作,你一定有充分的理由给这个软件做一个评价:

           好,不错。用户的需求基本得到了满足,在歌曲数量上符合大多数用户的搜索。

     


     

    1.2 需求分析

    • 列出该产品的典型用户和典型场景,并且用用例图(Use case)加以表示

     典型用户1

    姓名

    :林光涵

    年龄

    21

    生活费

    1300

    知识层次和能力

    :本科

    生活工作情况

    :大学在读

    动机

    :听歌休闲

    用户比例

    33%以上

    典型场景

    :宿舍,操场

    典型描述

    :宿舍睡觉听歌放松,跑步时听一些动感的音乐

            

    典型用户2

    姓名

    :吴建涛

    年龄

    28

    工资

    :8000

    知识层次和能力

    :高中

    生活工作情况

    :经商

    动机

    :听歌,观看音乐MV

    用户比例

    33%以上

    典型场景

    :商店,卧室

    典型描述

    :卧室睡觉前听歌放松

     

    目标用户 群体   特征 需求
     音乐用户 学生 年轻,时间比较充裕
    1. 能够便捷找到喜欢的音乐
    2. 对音乐单曲、专辑进行评论
    3. 分享自己喜欢的音乐
    4. 找到志同道合的小伙伴
     白领 有一定工作压力
     时尚人群 热爱音乐潮流
    IT人员 工作压力较大 
        音乐内容生产者 歌手 作品得到传播
    1.     扩大知名度和传播广度
    2. 增加收入
    3. 更多的展示自己,深入粉丝群体
     音乐人 作品得到发现与传播
    DJ  电台节目得到传播
    唱片公司  扩大知名度

     

    • 选择获取需求的方法(比如焦点小组、深入面谈),得到该软件的需求。(对不同的典型用户可以采取不同的方法)   

          通过深入面谈,我们可以总结出用户对于APP最基本的需求是实现听歌和搜索歌曲,深入面谈通常是一对一的采访,着重探究用户在使用软件时有哪些困难,并如何改进软件。另外也可以采用“焦点小组”的方法,将目标客户分成不同的小组,深入讨论用户想要什么,用户对软件的评价等等。将收集到的信息进行卡片分类,最大程度的统一用户对软件需求的认识,并量化各种特征,更好地定义一个软件的信息架构、用户的工作流程,软件菜单结构、网站的浏览路径、各种内容的层次关系等。

     


     

    1.3 分析

    • 尽可能地使用软件的所有功能。
    搜索歌曲

              
    搜索主页面信息导航只做了歌手分类,没有风格分类之类的导航,这样更简洁,给用户视觉带来舒适感,不会让用户因分类过多而不知如何选择的情况,搜索出来的结果有单曲、歌手、专辑、歌单等,支持各搜索结果来回左右滑动切换查看,操作十分便捷,搜索结果中歌曲信息的右下方的小三角支持直接下载或收藏,但是按钮区域太小,用户容易误操作,点击成播放歌曲[4]

     

    本地音乐
    四种不同的本地音乐组织形式能很好的满足用户用于整理音乐的需求,最右方的字母快速查询功能也很实用。

     

    收听电台
     
    有两个入口可以进入到收听电台页面,一个在主页,一个在我的音乐(前提是有关注电台),电台播放界面与音乐播放界面有些许不同,增加了赞和关注功能,将分享标签移至上方,下载标签移至左下方,这样是因为电台节目信息相对较多,要将一些优先级低的功能放置到用户主要的视觉注意力之外的地方,将更多的视觉高热度区域留给电台信息介绍。

     

    • 分析这个软件目前的优劣 (和类似软件相比),推理出这个软件团队在软件工程方面可以提高的重要方面 (具体建议)。要求把对比的结果列出一个表格,对比每个软件各自的优点和缺点。
    基本功能差别不大,关键区别在于能否听歌识曲
    网易云音乐缺少桌面歌词的功能
    网易云音乐和虾米音乐的呈现内容最为丰富

    优势:

    1. 云音乐小秘书满足了用户在之前各类音乐APP上从未满足过的反馈需求都能第一时间得到云音乐小秘书的人工回复,给了用户人性化的体验。
    2. 精准的私人FM推荐功能,比起豆瓣FM降低了重复率,算法更加准确,推荐更符合用户口味。
    3. 权限要求较少,不涉及到过多用户隐私,且退出APP后无后台进程留存,这一点比起大多音乐类APP难得可贵。
    4. 评论功能,听音乐看评论已经成为了许多用户的习惯,大部分听歌的用户都会有一个交流的需求,以来达到共鸣。

    不足之处:

    1. 主页面双层Tabs会让用户在使用的过程中产生一定的混乱感。
    2. 一些热门电视音乐节目的资源欠缺,没有抢占到一些独家资源,如“中国好声音”“我是歌手”等,一些最新专辑因为版权问题迟迟未能上线,这一点有时虾米会做得比较好。

    建议:

    1. 建立歌曲评论标准:通过歌曲评论标准,显示歌曲、歌单的评论偏向于好或坏、爱或恨,用户可以通过这样相对客观的标准为自己选择喜欢的歌。
    2. 关注页面改进建议:关注页面中的试听,建议直接在动态页面播放,而不进入播放页面;方便用户继续查看动态。
    3. 需要不断的提升自身产品的用户体验,反馈更好更快、曲库更全更广、界面更简洁更好用等等。

     

    • [附加题]根据理解和体验,画出整个软件所有功能逻辑框图,根据重要度标识出各模块的重要度、完成度、出发点及效果;
    音乐首页:
    重要度:95%
    完成度:80%
    出发点:软件的主体功能
    效果:引导用户的视觉

     

    我的音乐:
    重要度:95%
    完成度:90%
    出发点: 每款软件都必不可少的部分
    效果:栏目设置清晰合理,用户能够很快找到自己的需求

     

    我的音乐圈:
    重要度:70%
    完成度:80%
    出发点: 分享用户动态
    效果:一般

     

    快速入口:
    重要度:90%
    完成度:85%
    出发点: 便捷启动方式
    效果:不错

     

    搜索:
    重要度:70%
    完成度:90%
    出发点: 搜索喜欢的音乐
    效果:信息布局合理,用户理解起来成本低

     

     

    • [附加题]针对不同的维度评分,对用户体验方面、UI界面美观度、核心功能,分别打分(能给出更详细的评分更优)。
    维度  得分理由
    用户体验85前文举过部分用户体验的例子,结合后给出的评分
    UI界面美观度85整体美观度不错,界面简约,但是缺乏一些设计
    核心功能95在搜歌、下载以及观看音乐视频等核心功能方面还是做得不错的

     


     

    1.4 建议和规划

    • 如果你是项目经理,如何提高从而在竞争中胜出?
    1. 要能理解用户,站在用户的角度上考虑问题,观察用户不善于表达的需求。
    2. 要能够分析出项目中的重点。
    3. 要有一定的专业能力。
    4. 加强沟通能力,和团队成员并肩作战。

     

    • 目前市场上有什么样的产品了?
    1.  QQ音乐
    2. 酷狗音乐
    3. 酷我音乐
    4. 虾米音乐
    5. 多米音乐
    6. 豆瓣FM
    7. 百度音乐

     

    • 你要设计什么样的功能?(杀手功能 or 外围功能)

     

              我会选择设计外围功能/必要需求,即UI界面设计,因为一个友好的界面首先就会给用户留下一个良好的印象,其次用户才会更加深入的去使用这一款APP,在这一功能上,我觉得网易云音乐可以多去考察、了解用户的选择心理与爱好,从而设计出更加符合用户需求的界面。

     

    • 为何要做这个功能,而不是其他功能?

              其实现在市场上流行的听歌软件在搜索、发现音乐的功能上已经都非常的成熟了,因此我觉得设计更加友好、简约易懂的界面更能吸引用户的使用。

     

    • 为什么用户会用你的产品/功能?
    1. 转换成本低:网易云音乐APP支持从多米和豆瓣导入歌单,PC端则支持从虾米,豆瓣,酷狗,酷我,多米导入。
    2. 歌单功能:通过歌单的创作和分享来分类场景化的需求,不像豆瓣FM依赖算法来推荐歌曲,网易云音乐更多是通过“人”来进行歌单的传播和推荐,形成以歌单为核心的组织方式,使歌单、 DJ电台、推荐成为用户发现音乐的核心线索;也保留热歌榜、新歌榜等榜单作为辅助线索;此举涵盖传统用户行为方式,主打创新特色的歌曲发现方式。以社交化的分享和个性化推荐解决用户发现、寻找音乐的需求。
    3. 评论功能:通过UGC模式积攒大量歌曲用户评论,营造出音乐社区氛围感和归属感,提高用户活跃度。
    4. 移动云服务:网易云音乐目前为止已经涵盖安卓、iPhone、iPad、PC、Mac、WP六个版本。通过云服务,把音乐放在云端,让音乐无处不在。
    5. UI界面设计:整体UI简洁清晰,功能与操作恰到好处,几乎没有使用门槛,较少出现闪退、严重BUG现象。
    6. 娱乐化:网易云音乐结合自身资源,与明星、音乐人等做音乐访谈等近距离节目,可以增强粉丝与歌手、明星的了解和拉近两者距离,并增强用户对产品的粘性。

     

    • 你的创新在哪里? 请使用 NABCD [5]分析.

    N (Need 需求)

    1.  找到喜欢的音乐。
    2. 基于音乐的互动、寻求志同道合小伙伴。
    3. 分享音乐。

     

    A (Approach 做法)

    1.  强大的曲库保证了用户通过搜索来寻找音乐的需求,以及使用“私人FM”和“朋友”的功能,发现自己的好友和附近的人在听什么样的歌。
    2. 通过云音乐小秘书私信发送你关注的歌手的新专辑或单曲,并通过一定的数据分析手段,使用户通过“私人FM”和“个性化推荐”来获得符合自己口味的音乐。
    3. 在音乐品质上给用户更多的选择,满足用户对于消费音乐的场景和分类越来越多的需求,每一个歌单就是一个音乐的使用场景,用户可以根据不同场景来选择不同的歌单。

     

     B (Benefit  好处)

    1. 吸引更多的用户使用这一款软件。
    2. 随着用户的增多,可以与更多的公司合作推广 其他应用或者自己的产品,公司盈利也会逐渐上升。

     

     C (Competitors 竞争)

    1. 优势:网易云音乐庞大的用户量与强大的用户黏性。
    2. 劣势:商业平台不完善。

     

     D (Delivery 交付)  

    1.  可以利用 线下合作的商家以及线上官网,app广告进行推广。
    2.  扩大宣传,与运营商合作,推出一系列节省流量的用户体验服务收费吸引用户。

     

    • [附加题] 如果你来领导这个团队,会有什么不一样?
    1. UI界面应该会有比较大的提升。
    2. 站在用户的角度考虑,注重用户体验。
    3. 软件发布之前进行严格的测试。

     

    • [附加题] 如果你的团队有5个人, 4个月的时间,你作为项目经理,应该如何配置角色(开发,测试,美工等等)?
    人员配置人数
    项目经理1
    开发2
    测试1
    美工1

     

    周数任务
    1需求分析与市场调研
    2                 需求复审,设计原型,确定预期效果、项目开发安排、经费预估,并完成软件规格需求说明书。
    3搭建开发环境,确定编码规范,进行系统概要设计。
    4~5进行系统的详细设计,包括系统的基本处理流程、组织结构、模块划分、功能分配、接口设计、运行设计等等。
    6~12开发者根据设计要求分别实现各个模块的功能,并对各功能模块进行单元测试和集成测试。
    13~15优化、修复软件内测中发现的bug,并追踪是否有需求变更。
    16发布正式版本,交付使用。

     


     

    1.5 其他你愿意跟我们分享的内容

             花了快两天的时间写了这一篇博文,查阅了许多资料去了解网易云音乐这一款APP,它从设计到投入使用仅仅花了短短一年的时间,却能够做到与其他听歌软件几近相当的成绩,从网易云大的团体映射到我们个人,是不是同样可以说明一个人只要付出努力,就可以在更短的时间内做到与别人一样的程度,当然前提是你要用心的付出。时光易老,且行且珍惜,大学时光一去不回,人生仍需奋斗。

     


     

    参考资料:

    [1]  https://wenku.baidu.com/view/e3ff473b700abb68a882fbc0.html.网易云音乐的发展历程

    [2] http://www.jianshu.com/p/d3fe9a6e39a7.网易云音乐APP分析报告

    [3] https://wenku.baidu.com/view/05c04251ad02de80d4d8408e.html .MP3技术与美国音乐产业演化

    [4] http://www.jianshu.com/p/d3fe9a6e39a7.网易云音乐APP分析报告

    [5] http://www.cnblogs.com/xinz/archive/2010/12/01/1893323.html.现代软件工程将讲义

     

    转载于:https://www.cnblogs.com/lrr123/p/7631788.html

    展开全文
  • 小甲鱼零基础入门学习python笔记

    万次阅读 多人点赞 2019-08-14 11:06:30
    小甲鱼老师零基础入门学习Python全套资料百度(包括小甲鱼零基础入门学习Python全套视频+全套源码+全套PPT课件+全套课后题及Python常用工具包链接、电子书籍等)请往我的资源... 000 愉快的开始 ...

    小甲鱼老师零基础入门学习Python全套资料百度云(包括小甲鱼零基础入门学习Python全套视频+全套源码+全套PPT课件+全套课后题及Python常用工具包链接、电子书籍等)请往我的资源(https://download.csdn.net/download/qq_32809093/13099592查看

    目录:

    000 愉快的开始
    001 我和Python的第一次亲密接触
    002 用Python设计第一个游戏
    003 小插曲之变量和字符串
    004 改进我们的小游戏
    005 闲聊之Python的数据类型
    006 Pyhon之常用操作符
    007 了不起的分支和循环
    008 了不起的分支和循环2
    009 了不起的分支和循环3
    010 列表:一个打了激素的数组
    011列表:一个打了激素的数组2
    012列表:一个打了激素的数组3
    013元组:戴上了枷锁的列表
    014字符串:各种奇葩的内置方法
    015字符串:格式化
    016 序列!序列!
    017函数:Python的乐高积木
    018 函数:灵活即强大
    019函数:我的地盘听我的(局部变量与全局变量)
    020函数:内嵌函数和闭包
    021函数:lambda表达式
    022 函数:递归是神马
    023 递归:这帮小兔崽子
    024 递归:汉诺塔
    025 字典:当索引不好用时
    026 字典:当索引不好用时2
    027 集合:在我的世界里,你就是唯一
    028 文件:因为懂你,所以永恒
    029 文件:一个任务
    030 文件系统:介绍一个高大上的东西
    031 永久存储:腌制一缸美味的泡菜(pickle)
    032 异常处理:你不可能总是对的
    033 异常处理:你不可能总是对的2
    034 丰富的else语句及简洁的with语句
    035 图形用户界面入门:EasyGui
    036 类和对象:给大家介绍对象
    037 类和对象:面向对象编程
    038 类和对象:继承
    039 类和对象:拾遗
    040 类和对象:一些相关的BIF
    041 魔法方法:构造和析构
    042 魔法方法:算术运算
    043 魔法方法:算术运算2
    044 魔法方法:简单定制
    045 魔法方法:属性访问
    046 魔法方法:描述符(Property的原理)
    047 魔法方法:定制序列
    048 魔法方法:迭代器
    049 乱入:生成器
    050 模块:模块就是程序
    051 模块:__name__='__main__'、搜索路径和包
    052 模块:像个极客一样去思考
    053 论一只爬虫的自我修养
    054 论一只爬虫的自我修养2:实战
    055 论一只爬虫的自我修养3:隐藏

    064 GUI的终极选择:Tkinter
    065 GUI的终极选择:Tkinter2
    066 GUI的终极选择:Tkinter3
    067 GUI的终极选择:Tkinter4
    068 GUI的终极选择:Tkinter5
    069 GUI的终极选择:Tkinter6
    070 GUI的终极选择:Tkinter7
    071 GUI的终极选择:Tkinter8
    073 GUI的终极选择:Tkinter10
    074  GUI的终极选择:Tkinter11
    075 GUI的终极选择:Tkinter12
    076 GUI的终极选择:Tkinter13
    077 GUI的终极选择:Tkinter14

    078 Pygame:初次见面,请大家多多关照

     

    000 愉快的开始

    python跨平台。
    应用范围:操作系统、WEB、3D动画、企业应用、云计算
    大家可以学到什么Python3的所有常用语法、面向对象编程思维、运用模块进行编程、游戏编程、计算机仿真

    Python 是脚本语言
    脚本语言(Scripting language)是电脑编程语言,因此也能让开发者藉以编写出让电脑听命行事的程序。以简单的方式快速完成某些复杂的事情通常是创造脚本语言的重要原则,基于这项原则,使得脚本语言通常比 C 语言、C++语言 或 Java 之类的系统编程语言要简单容易。也让脚本语言另有一些属于脚本语言的特性:
    •  语法和结构通常比较简单
    •  学习和使用通常比较简单
    •  通常以容易修改程序的“解释”作为运行方式,而不需要“编译”
    •  程序的开发产能优于运行性能
    一个脚本可以使得本来要用键盘进行的相互式操作自动化。一个 Shell 脚本主要由原本需要在命令行输入的命令组成,或在一个文本编辑器中,用户可以使用脚本来把一些常用的操作组合成一组串行。主要用来书写这种脚本的语言叫做脚本语言。很多脚本
    语言实际上已经超过简单的用户命令串行的指令,还可以编写更复杂的程序。

    IDLE 是一个 Python Shell,shell 的意思就是“外壳”,基本上来说,就是一个通过键入文本与程序交互的途径!像我们 Windows 那个 cmd 窗口,像 Linux 那个黑乎乎的命令窗口,他们都是 shell,利用他们,我们就可以给操作系统下达命令。同样的,我们可以利用 IDLE 这个 shell 与 Python 进行互动。

    注:在 Python 中不能把两个完全不同的东西加在一起,比如说数字和文本

    如果我需要在一个字符串中嵌入一个双引号,正确的做法是:你可以利用反斜杠(\)对双引号转义:\",或者用单引号引起这个字符串

    001 我和Python的第一次亲密接触

    IDLE启动Python

    IDLE是一个Python Shellshell的意思就是“外壳”,基本上来说,就是一个通过键入文本与程序交互的途径

    我们看到>>>这个提示符,Ta的含义是告诉你,Python已经准备好了,在等着你键入Python指令呢

    好了,大家试试在IDLE里输入:

    >>>print (“I love fishc.com”)

    我们尝试点儿新的东西,我们输入

    >>>print(5+3)

    或者直接输入

    >>>5+3

    不妨在试试计算

    >>>1234567890987654321*987654321012345678

    还有我们可以将两个字符串“相加”在一起,这种做法叫做拼接字符串

    >>>print("well water"+"river")

    先试试

    >>> print("I love fishc.com"*2)

    >>> print("I love fishc.com\n"* 2)

    002 用Python设计第一个游戏

    实例1:

    print("---------我爱鱼C工作室----------")
    temp = input("不妨猜一下小甲鱼现在心里想的是哪个数字:")
    guess = int(temp)
    if guess == 8:
        print("我草,你是小甲鱼心里的蛔虫嘛?!")
        print("哼,猜中了也没有奖励!")
    else:
        print("猜错了,小甲鱼现在心里想的是8!")
        print("游戏结束,不玩啦")

    BIF 就是 Built-in Functions,内置函数。为了方便程序员快速编写脚本程序(脚本就是要编程速度快快快!!!),Python 提供了非常丰富的内置函数,我们只需要直接调用即可,例如 print() 的功能是“打印到屏幕”,input() 的作用是接收用户输入。

    在 Python 或 IDLE 中,输入 dir(__builtins__) 可以看到 Python 提供的内置方法列表(注意,builtins 前后是两个下划线哦)其中小写的就是 BIF。如果想具体查看某个BIF 的功能,比如 input(),可以在 shell 中输入 help(input),就会得到这个 BIF 的功能描述。哦,答案应该是 68 个

    >>> dir(__builtins__)  查看 Python 提供的内置方法列表

    >>> help(input)  查看input的具体使用说明 

    注:

    只有当标识符已经赋值后( Python 的变量是不用先声明的)才能在代码中使用,未赋值的标识符直接使用会导致运行时错误

    缩进是 Python 的灵魂

    Python 不允许 if 条件中赋值,所以 if c = 1: 会报错!

     

    003 小插曲之变量和字符串

    插曲之变量

    变量名就像我们现实社会的名字,把一个值赋值给一个名字时,Ta会存储在内存中,称之为变量(variable),在大多数语言中,都把这种行为称为“给变量赋值”或“把值存储在变量中”。

    不过Python与大多数其他计算机语言的做法稍有不同,Ta并不是把值存储在变量中,而更像是把名字贴在值的上边。

    所以有些Python程序员会说“Python”没有“变量”,只有“名字”。

    需要注意的地方

    在使用变量之前,需要对其先赋值。

    变量名可以包括字母、数字、下划线,但变量名不能以数字开头

    字母可以是大写或小写,但大小写是不同的。也就是说fishcFishC对于Python来说是完全不同的两个名字

    等号(=)是赋值的意思,左边是名字,右边是值,不可写反咯。

    插曲之字符串

    到目前为止,我们所认知的字符串就是引号内的一切东西,我们也把字符串叫做文本,文本和数字是截然不同的,咱看例子:>>>5+8

    >>> '5'+'8'

    要告诉Python你在创建一个字符串,就要在字符两边加上引号,可以是单引号或者双引号Python女士表示不挑剔。但必须成对,你不能一边单引号,另一边却花心的用上双引号结尾。

    如果字符串中需要出现单引号或双引号怎么办

    例如我想打印字符串:Let’s go!

    有两种方法,第一种比较常用,就是使用我们的转义符号(\)对字符串中的引号进行转义:

    >>> 'Let\'s go!'

    原始字符串

    好像反斜杠是一个好东西,但不妨试试打印:

    >>> str = 'C:\now'

    我们可以用反斜杠对自身进行转义:

    >>> str = 'C:\\now'

    原始字符串的使用非常简单,只需要在字符串前边加一个英文字母r即可(则都会以原始字符串输出):

    >>>str = r'C:\now'

    长字符串

    如果希望得到一个跨越多行的字符串,例如:

    我爱鱼C

    正如我爱小甲鱼,

    久久不肯散去……

    这我们就需要使用到三重引号字符串!

     

    004 改进我们的小游戏

    第一个改进要求:猜错的时候程序提示用户当前的输入比答案大了还是小了

    与操作and

    第二个改进要求:程序应该提供多次机会给用户猜测,专业点来讲就是程序需要重复运行某些代码。

    条件分支

    while循环

    实例1:找8

    temp = input("请输入一个数据:")
    guess = int(temp)
    i=0
    while guess != 8 and i < 3:
        i = i + 1
        temp = input("哎呀,猜错了,请重新输入吧:")
        guess = int(temp)
        if guess == 8:
            print("我草,你是小甲鱼心里的蛔虫嘛?")
            print("哼,猜对了也没有奖励")
        else:
            if guess > 8:
                print("哥,大了大了~~")
            else:
                print("嘿,小了!小了!!")
    print("游戏结束,不玩啦~~")

    random模块里边有一个函数叫做:randint()Ta会返回一个随机的整数。

    实例2:找随机数

    import random#导入随机数函数
    secret = random.randint(1,5)#随机生成1到5的一个随机数
    temp = input("请输入一个1-5的数据:")
    guess = int(temp)
    i=0
    while guess != secret and i < 6:
        i = i + 1
        guess = int(temp)
        if guess == secret:
            print("我草,你是小甲鱼心里的蛔虫嘛?")
            print("哼,猜对了也没有奖励")
        else:
            if guess > secret:
                print("哥,大了大了~~")
            else:
                print("嘿,小了!小了!!")      
            temp = input("请重新输入吧:")
    print("游戏结束,不玩啦~~")

     

    005 闲聊之Python的数据类型

    Python的一些数值类型:整型、布尔类型(True与False)、浮点型、e记法、复数类型等

    e记法(e4相当于10的四次方,e-10相当于10的-10次方)

    类型转换

    字符型转换为整型

    其它同上

    type()函数(可查看变量类型)

    isinstance()函数(用来判断两个输入参数类型是否一致)

     

    006 Pyhon之常用操作符

    算术操作符

    注:python中 \ 为除法, \\ 为整除 ,% 为取余

    幂运算(3的二次方)

    3的二次方后取负

    注:先幂运算、然后乘除、后加减、后逻辑

    3的负二次方

    比较操作符

    逻辑操作符

    优先级问题

    007 了不起的分支和循环

    打飞机游戏框架:

    加载背景音乐

    播放背景音乐(设置单曲循环)

    我方飞机诞生

    while True:
        if 用户是否点击了关闭按钮:
           推出程序
           
        interval += 1;
        if interval == 50:
           interval = 0;
           小飞机诞生
        小飞机移动一个位置
        屏幕刷新
        
        if 用户鼠标产生移动:
           我方飞机中心位置 = 用户鼠标位置
           屏幕刷新
           
        if 我方飞机与小飞机发生肢体冲突:
           我方挂,播放撞机音乐
           修改我方飞机图案
           打印“Game over"
           停止背景音乐,最好淡出

     

    008 了不起的分支和循环2

    现在小甲鱼来考考大家:

    按照100分制,90分以上成绩为A8090B6080C60以下为D,写一个程序,当用户输入分数,自动转换为ABCD的形式打印。

    score = int(input('请输入一个分数:'))
    if 100 >= score >= 90:
        print('A')
    elif 90 > score >= 80:
        print('B')
    elif 80 > score >= 60:
        print('C')
    elif 60 > score >= 0:
        print('D')
    else:
        print('输入错误!')

    条件表达式(三元操作符)

    有了这个三元操作符的条件表达式,你可以使用一条语句来完成以下的条件判断和赋值操作:

    x, y = 4, 5

    if x < y:

      small = x

    else:

      small = y

    例子可以改进为

    small = x if x < y else y    #如果x小于y,则small等于x,否则等于y

    断言(assert)

    assert这个关键字我们称之为“断言”,当这个关键字后边的条件为假的时候,程序自动崩溃并抛出AssertionError的异常。

    举个例子:

    >>> assert 3 > 4

    一般来说我们可以用Ta再程序中置入检查点,当需要确保程序中的某个条件一定为真才能让程序正常工作的话,assert关键字就非常有用了

     

    009 了不起的分支和循环3

    while循环

    while 条件:

                      循环体

    for循环

    虽然说Python是由C语言编写而来的,但是Tafor循环跟C语言的for循环不太一样,Pythonfor循环显得更为智能和强大!

    语法:

    for 目标 in 表达式:

         循环体

    每次取FishC中一个字符及空格输出

    range()函数

    语法:range( [strat],[stop],[step] )

    这个BIF有三个参数,其中用中括号括起来的两个表示这两个参数是可选的。

    step=1表示第三个参数的值默认值是1setp为每步距离

    range这个BIF的作用是生成一个从start参数的值开始到stop参数的值结束的数字序列

     

    break语句(结束本层循环)

    实例:

    bingo = '小甲鱼是帅哥'
    answer = input('请输入小甲鱼最想听的一句话:')

    while True:
        if answer == bingo:
            break
        answer = input('抱歉,错了,请重新输入(答案正确才能退出游戏):')

    print('哎哟,帅哦~')
    print('您真是小甲鱼肚子里的蛔虫啊^_^')

    continue语句(当前位置结束本次循环,重新开始下次循环)

    实例:

    for i in range(10):
        if i%2 != 0:
            print(i)
            continue
        i += 2
        print(i)

    010 列表:一个打了激素的数组

    列表一个打了激素的数组

    创建列表

    创建一个普通列表

    创建一个混合列表

    创建一个空列表

    向列表添加元素

    append()函数向列表末尾添加一个元素

    extend()函数向列表末尾添加多个元素

    insert(n,xxx)函数向列表中第n个元素前插入一个元素

    注:0表示第一个元素

    011列表:一个打了激素的数组2

    从列表中获取元素

    跟数组一样,我们可以通过元素的索引值(index)从列表获取单个元素,注意,列表索引值是从 0 开始的。

    从列表删除元素

    remove()函数表示从列表中删除某个元素

    del()函数也表示从列表中删除某个元素

    pop()函数从列表中取出最后一个元素

    列表分片(Slice

    利用索引值,每次我们可以从列表获取一个元素,但是我们总是贪心的,如果一次性需要获取多个元素,有没有办法实现呢?利用列表分片,我们可以简单的实现这个要求。

    member[0:2]表示从第1个元素开始拷贝,一共拷贝两个元素,即member[0]和member[1]

    列表的拷贝

    012列表:一个打了激素的数组3

    列表的一些常用操作符

    比较操作符

    逻辑操作符

    连接操作符

    重复操作符

    成员关系操作符

    关于分片“拷贝”概念的补充

    >>> dir(list)可查看所有列表的操作函数

    count()函数可计算列表中相同元素个数

    index()函数可索引列表元素

    reverse()将列表中元素倒序

    sort()将列表中元素从小到大排序

    关于分片“拷贝”概念的补充

    注:list13=list11相当于多了个指向列表的标签,list12 = list[:]是实实在在的拷贝

    013元组:戴上了枷锁的列表

    由于和列表是近亲关系,所以元组和列表在实际使用上是非常相似的。

    我们这节课主要通过讨论元组和列表到底有什么不同来学习元组,酱紫大家就不会觉得老是重复一样的内容

    我们主要从以下几个点来讨论学习:

    创键和访问一个元组

    创建元组(括号可以没有,但逗号一定要有)

    访问元组前两个元素

    更新和删除一个元组

    更新一个元组

    注:其并未对原元组进行修改,而是生成了一个新的元组,并贴上temp名字标签而已。原元组由于标签没有了,则会被自动回收。

    删除一个元组

    元组相关的操作符

    注:元组不允许修改和删除。

    014字符串:各种奇葩的内置方法

     

    015字符串:格式化

    由于花括号被解释掉,所以不打印后面中文

    字符串格式化符号含义

    将ASCII码97对应的字符输出

    格式化整数

    格式化操作符辅助命令

    5表示输出为五位数

    Python 的转义字符及其含义

     

    016 序列!序列!

    列表、元组和字符串的共同点

    都可以通过索引得到每一个元素

    默认索引值总是从0开始

    可以通过分片的方法得到一个范围内的元素的集合

    有很多共同的操作符(重复操作符、拼接操作符、成员关系操作符)

    使用list方法

    元组转换为列表

    注:元组为小括号,列表为中括号。

    max() 返回序列或者参数集合中的最大值

    min() 返回序列或者参数集合中的最小值

    sum(iterable[,start=0]) 返回序列iterable和可选参数start的总和

    sorted()将元素从小到大重新排列

    reversed()将元素倒序排列

    注:元组是不可以修改和删除的,所以不可以直接对元组使用sorted与reversed命令

    enumerate()将每个元素插入枚举

    zip()返回由各个参数的序列组成的元组

     

    017函数:Python的乐高积木

    定义一个函数和调用

     

    018 函数:灵活即强大

    形参和实参

    >>> def MyFirstFunction(name):

      '函数定义过程中的name是叫形参'

      #因为Ta只是一个形式,表示占据一个参数位置

      print('传递进来的' + name + '叫做实参,因为Ta是具体的参数值!')

    >>> MyFirstFunction('小甲鱼')

    传递进来的小甲鱼叫做实参,因为Ta是具体的参数值!

    关键字参数

    默认参数(即形参中给定默认值,则在未给实参时会以默认值输出)

    收集参数

     

    019函数:我的地盘听我的

    函数与过程

    再谈谈返回值

    如果有返回值,函数则返回对应值;如果没有,则返回None

    可以返回多个值

    019函数:我的地盘听我的(局部变量与全局变量)

    def discounts(price, rate):
        final_price = price * rate
        old_price = 88 #这里试图修改全局变量
        print('修改后old_price的值是:', old_price)
        return final_price

    old_price = float(input('请输入原价:'))
    rate = float(input('请输入折扣率:'))
    new_price = discounts(old_price, rate)
    print('修改后old_price的值是:', old_price)
    print('打折后价格是:', new_price)

    global可将局部变量声明为全局变量

    020函数:内嵌函数和闭包

    内嵌函数

    闭包(closure

    注:使用nonlocal语句将x强制为不是局部变量

    021函数:lambda表达式

    lambda表达式的作用

    Python写一些执行脚本时,使用lambda就可以省下定义函数过程,比如说我们只是需要写个简单的脚本来管理服务器时间,我们就不需要专门定义一个函数然后再写调用,使用lambda就可以使得代码更加精简

    对于一些比较抽象并且整个程序执行下来只需要调用一两次的函数,有时候给函数起个名字也是比较头疼的问题,使用lambda就不需要考虑命名的问题了

    简化代码的可读性,由于普通的屌丝函数阅读经常要跳到开头def定义部分,使用lambda函数可以省去这样的步骤。

    过滤函数filter可筛选出非零元素

    筛选出奇数

    注:lambda x:x%2用来判断是否为奇,x为奇则输出1,否则输出0;range(10)可生成0-9的10个整数,filter用来筛选非零元素;如果为偶数,则被筛选掉;如果为奇数,则保留,但输出的是rang(10)产生的原始数,因为lambda只是用来判断是否为奇偶

    range生成的0-9给了x,x经过2倍运算后再赋值给x

    022 函数:递归是神马

    汉诺塔游戏

    树结构的定义

    谢尔宾斯基三角形

    递归求阶乘

    写一个求阶乘的函数

    正整数阶乘指从1乘以2乘以3乘以4一直乘到所要求的数。

    例如所给的数是5,则阶乘式是1×2×3×4×5,得到的积是120,所以120就是4的阶乘。

    假设我们n的值传入是5,那么:

    实例:求阶乘

    def factorial(n):
        result = n
        for i in range(1, n):
            result *= i

        return result

    number = int(input('请输入一个正整数:'))
    result = factorial(number)
    print("%d 的阶乘是:%d"  % (number, result))#格式化为整数类型

    实例2:递归求阶乘

    def factorial(n):
        if n == 1:
            return 1
        else:
            return n * factorial(n-1)

    number = int(input('请输入一个正整数:'))
    result = factorial(number)
    print("%d 的阶乘是:%d" % (number, result))

    023 递归:这帮小兔崽子

    坑爹的兔子

    斐波那契数列的迭代实现

    我们都知道兔子繁殖能力是惊人的,如下图:

    我们可以用数学函数来定义:

    课间练习:假设我们需要求出经历了20个月后,总共有多少对小兔崽子?(迭代 vs 递归

    def fab(n):
        n1 = 1
        n2 = 1
        n3 = 1

        if n < 1:
            print('输入有误!')
            return -1

        while (n-2) > 0:
            n3 = n2 + n1
            n1 = n2
            n2 = n3
            n -= 1
        
        return n3

    result = fab(20)
    if result != -1:
        print('总共有%d对小兔崽子诞生!' % result)

    斐波那契数列的递归实现

    递归实现(递归计算时间将拉长)

    def fab(n):
        if n < 1:
            print('输入有误!')
            return -1

        if n == 1 or n == 2:
            return 1
        else:
            return fab(n-1) + fab(n-2)

    result = fab(35)
    if result != -1:
        print('总共有%d对小兔崽子诞生!' % result)

    注:迭代计算时间远比递归少,因为递归要循环出入栈

    024 递归:汉诺塔

    递归求解汉诺塔

     

    对于游戏的玩法,我们可以简单分解为三个步骤

    将前63个盘子从X移动到Y上。

    将最底下的第64个盘子从X移动到Z上。

    Y上的63个盘子移动到Z上。

    问题一:将X上的63个盘子借助Z移到Y上;

    问题二:将Y上的63个盘子借助X移到Z上。

     

    对于游戏的玩法,我们可以简单分解为三个步骤

    将前63个盘子从X移动到Y上。

    将最底下的第64个盘子从X移动到Z上。

    Y上的63个盘子移动到Z上。

    问题一:将X上的63个盘子借助Z移到Y上;

    问题二:将Y上的63个盘子借助X移到Z上。

    实例:

    def hanoi(n, x, y, z):
        if n == 1:
            print(x, ' --> ', z)
        else:
            hanoi(n-1, x, z, y) # 将前n-1个盘子从x移动到y上
            print(x, ' --> ', z) # 将最底下的最后一个盘子从x移动到z上
            hanoi(n-1, y, x, z) # 将y上的n-1个盘子移动到z上

    n = int(input('请输入汉诺塔的层数:'))
    hanoi(n, 'X', 'Y', 'Z')

    025 字典:当索引不好用时

    映射

    创建和访问字典

    >>> dict4 = dict(小甲鱼='让编程改变世界',李宁='一切皆有可能')
    >>> dict4
    {'小甲鱼': '让编程改变世界', '李宁': '一切皆有可能'}

    >>> dict4['爱迪生'] = '天才是99%的汗水加1%的灵感'
    >>> dict4
    {'小甲鱼': '让编程改变世界', '李宁': '一切皆有可能', '爱迪生': '天才是99%的汗水加1%的灵感'}

    026 字典:当索引不好用时2

    fromkey()方法用于创建并返回一个新的字典它有两个参数,第一个参数是字典的键;第二个参数是可选的,是传入键的值。如果不提供,默认是None

    >>> dict1 = {}
    >>> dict1.fromkeys((1,2,3))
    {1: None, 2: None, 3: None}
    >>> dict2 = {}
    >>> dict2.fromkeys((1,2,3),"Number")
    {1: 'Number', 2: 'Number', 3: 'Number'}
    >>> dict3 = {}
    >>> dict3.fromkeys((1,2,3),('one','two','three'))
    {1: ('one', 'two', 'three'), 2: ('one', 'two', 'three'), 3: ('one', 'two', 'three')}

    访问字典的方法有key()、values()和items()

    key()用于返回字典中的键,value()用于返回字典中所有的值,item()当然就是返回字典中所有的键值对(也就是项)

    >>> dict1 = dict1.fromkeys(range(5),'赞')
    >>> dict1.keys()
    dict_keys([0, 1, 2, 3, 4])
    >>> dict1.values()
    dict_values(['赞', '赞', '赞', '赞', '赞'])
    >>> dict1.items()
    dict_items([(0, '赞'), (1, '赞'), (2, '赞'), (3, '赞'), (4, '赞')])

    get()方法提供了更宽松的方式去访问字典项,当键不存在的时候,get()方法并不会报错,只是默默第返回一个None,表示啥都没找到:

    >>> dict1.get(10)
    >>> dict1.get(4)
    '赞'

    如果希望找不到数据时返回指定的值,可以在第二个参数设置对应的默认返回值:

    >>> dict1.get(32,'木有')
    '木有'

    如果不知道一个键是否在字典中,可以使用成员资格操作符(in 或 not in)来判断
    >>> 31 in dict1
    False
    >>> 4 in dict1

    clear()可清空一个字典

    >>> dict1
    {0: '赞', 1: '赞', 2: '赞', 3: '赞', 4: '赞'}
    >>> dict1.clear()
    >>> dict1
    {}

    copy()方法是复制字典(全拷贝)

    >>> a = {1:'one',2:'two',3:'three'}
    >>> b = a.copy()
    >>> id(a)
    52448840
    >>> id(b)
    52503624
    >>> a[1] = 'four'
    >>> a
    {1: 'four', 2: 'two', 3: 'three'}
    >>> b
    {1: 'one', 2: 'two', 3: 'three'}

    pop()是给定键弹出对应的值,popitem()是随机弹出一个项

    >>> a.pop(2)
    'two'
    >>> a
    {1: 'four', 3: 'three'}
    >>> a.popitem()
    (1, 'four')
    >>> a
    {3: 'three'}

    setdefault()方法与get()方法相似,但setdefault()在字典中找不到相应的键值时会自动添加

    >>> a = {1:'one',2:'two',3:'three'}
    >>> a.setdefault(2)
    'two'
    >>> a.setdefault(4)
    >>> a
    {1: 'one', 2: 'two', 3: 'three', 4: None}

    update()方法可以更新字典

    >>> a = {1:'one','小白':None}

    >>> b = {'小白':'狗'}
    >>> a.update(b)
    >>> a
    {1: 'one', '小白': '狗'}

    027 集合:在我的世界里,你就是唯一

    字典的表亲--集合(在python3中,如果用大括号括起一堆数字但没有体现映射关系,那么就会认为这堆玩意儿就是个集合)

    >>> num1 = {}
    >>> type(num1)
    <class 'dict'>
    >>> num2 = {1,3,4}
    >>> type(num2)
    <class 'set'>

    集合中的元素都是唯一的(集合会自动帮我们把重复的数据清理掉,集合是无序的,所以不能试图去索引集合中的某一个元素

    >>> num = {1,2,3,4,5,5,4,3,2,1}
    >>> num
    {1, 2, 3, 4, 5}

    如何创建一个集合有两种方法:1、直接把一堆元素用大括号括起来;2、用set()

    一种是直接把一堆元素用花括号括起来

    >>> set1 = {'小甲鱼','小鱿鱼','小甲鱼'}

    一种是使用set()工厂函数

    >>> set2 = set(['小甲鱼','小鱿鱼','小甲鱼'])
    >>> set1 == set2
    True

    课堂搞搞看

    要求:去掉列表中重复的元素

    [0, 1, 2, 3, 4, 5, 5, 3, 1]

    方法一、

    >>> list1 = [1,2,3,4,5,5,3,1,0]

    >>> temp = list1[:]
    >>> list1.clear()
    >>> list1
    []
    >>> for each in temp:
        if each not in list1:
            list1.append(each) #append()表示向列表中添加元素

    方法二、

    >>> list1 = list(set(list1))
    >>> list1
    [0, 1, 2, 3, 4, 5]

    #set(list1)先将list1列表转变为集合, list(set(list1))再讲集合转变为列表

    如何访问集合中的值

    由于集合中的元素是无序的,所以并不能像序列那样用下标来进行访问,但是可以使用迭代把集合中的数据一个个读取出来

    可以使用for把集合中的数据一个个读取出来

    >>> set1 = {1,2,3,4,5,4,3,2,1,0}
    >>> for each in set1:
        print(each,end = ' ')

        
    0 1 2 3 4 5 

    •也可以通过innot in判断一个元素是否在集合中已经存在

    >>> 0 in set1
    True
    >>> 8 in set1
    False

    使用add()方法可以为集合添加元素,使用remove()方法可以删除集合中已知的元素:

    >>> set1.add(6)
    >>> set1
    {0, 1, 2, 3, 4, 5, 6}
    >>> set1.remove(5)
    >>> set1
    {0, 1, 2, 3, 4, 6}

    不可变集合(把元素给froze冰冻起来)(像元组一样不能随意地增加或删除集合中的元素)

    028 文件:因为懂你,所以永恒

    大多数u程序都是:首先接收输入数据,然后按照要求进行处理,最后输出数据

    虽然当前数据放在内存中存取的速度要比硬盘中快,但一旦断电则会丢失,所以尽量ctrl+s保持到硬盘中


    什么是文件

    打开文件

    open(file, mode='r', buffering=-1, encoding=None,errors=None, newline=None, closefd=True, opener=None)

    open()的第一个参数是传入的文件名,第二个参数是指定文件的打开模式

    文件对象方法

    >>> f = open("D:\\python3.3.2\Hello.txt")
    >>> f
    <_io.TextIOWrapper name='D:\\python3.3.2\\Hello.txt' mode='r' encoding='cp936'>
    >>> f.read()
    "A. HISTORY OF THE SOFTWARE\n==========================\n\nPython was created in the early 1990s by Guido van Rossum at Stichting\nMathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands\nas a successor of a language called ABC.  Guido remains Python's\nprincipal author, although it includes many contributions from others.\n\nIn 1995, Guido continued his work on Python at the Corporation for\nNational Research Initiatives (CNRI, see http://www.cnri.reston.va.us)\nin Reston, Virginia where he released several versions of the\nsoftware."
    >>> f.close()
    >>> f = open("D:\\python3.3.2\Hello.txt")
    >>> f.read(5)
    'A. HI'
    >>> f.tell()   #返回当前光标所在文件的位置
    5
    >>> f.readline()
    'STORY OF THE SOFTWARE\n'
    将f放入到列表

    >>> f = open("D:\\python3.3.2\Hello.txt",'w')#w模式写入会覆盖已存在的文件(即原文件内容全部被删除),a模式则在末尾追加写入
    >>> f.write('who are you')          #返回的是写入的字符数
    11
    >>> f.close()

    029 文件:一个任务

    任务:将文件(record.txt)中的数据进行分割并按照以下规律保存起来:

    小甲鱼的对话单独保存为boy_*.txt的文件(去掉“小甲鱼:”)

    小客服的对话单独保存为girl_*.txt的文件(去掉“小客服:”)

    文件中总共有三段对话,分别保存为boy_1.txt, girl_1.txtboy_2.txt, girl_2.txt, boy_3.txt, gril_3.txt6个文件(提示:文件中不同的对话间已经使用“==========分割

    test1:

    f = open("record.txt")

    boy = []
    girl = []
    count = 1

    for each_line in f:
        if each_line[:6] != '======':#判断是否连续读到六个=
            (role,line_spoken) = each_line.split(':',1)#split以:进行字符切割,
            #将切得到的两部分内容依次存放在role与line_spoken中
            if role == '小甲鱼':
                boy.append(line_spoken)#将小甲鱼说的内容添加到列表boy中
            if role == '小客服':
                girl.append(line_spoken)#将小客服说的内容添加到列表girl中
        else:
            file_name_boy = 'boy_' + str(count) + '.txt'
            file_name_girl = 'girl_' + str(count) + '.txt'

            boy_file = open(file_name_boy,'w')#以w模式新建一个以file_name_boy命名的txt文件
            girl_file = open(file_name_girl,'w')#并贴上boy_file的标签

            boy_file.writelines(boy)#将列表boy中的内容写入到boy_file文件中
            girl_file.writelines(girl)

            boy_file.close()#关闭boy_file文件
            girl_file.close()

            boy = []#清空列表boy
            girl = []
            count += 1

    file_name_boy = 'boy_' + str(count) + '.txt'
    file_name_girl = 'girl_' + str(count) + '.txt'

    boy_file = open(file_name_boy,'w')
    girl_file = open(file_name_girl,'w')

    boy_file.writelines(boy)
    girl_file.writelines(girl)

    boy_file.close()
    girl_file.close()#记得关闭文件

    test2:

     

    def save_file(boy,girl,count):
        file_name_boy = 'boy_' + str(count) + '.txt'
        file_name_girl = 'girl_' + str(count) + '.txt'

        boy_file = open(file_name_boy,'w')
        girl_file = open(file_name_girl,'w')

        boy_file.writelines(boy)
        girl_file.writelines(girl)

        boy_file.close()
        girl_file.close()

    def split_file(file_name):
        f = open(file_name)

        boy = []
        girl = []
        count = 1

        for each_line in f:
            if each_line[:6] != '======':
                (role,line_spoken) = each_line.split(':',1)#split以:进行字符切割,
                #将切得到的两部分内容依次存放在role与line_spoken中
                if role == '小甲鱼':
                    boy.append(line_spoken)
                if role == '小客服':
                    girl.append(line_spoken)
            else:
                save_file(boy,girl,count)

                boy = []
                girl = []
                count += 1


        save_file(boy,girl,count)
        f.close()

    split_file('record.txt')

    030 文件系统:介绍一个高大上的东西

    os模块中关于文件/目录常用的函数使用方法

    >>> import os
    >>> os.getcwd()
    'D:\\python3.3.2\\小甲鱼python\\python程序\\第二十九课'

    >>> os.listdir('D:\\python3.3.2\\小甲鱼python\\python程序\\第二十九课')
    ['boy_1.txt', 'boy_2.txt', 'boy_3.txt', 'girl_1.txt', 'girl_2.txt', 'girl_3.txt', 'record.txt', 'test.py', 'test2.py']

    os.path模块中关于路径常用的函数使用方法

     >>> os.path.getsize('python.exe')  #获取文件的尺寸,返回值以字节为单位


    031 永久存储:腌制一缸美味的泡菜(pickle)

    python提供了一个标准的模块pickle可以非常容易地将列表、字典这类复杂的数据类型存储为文件。它几乎可以把所有python的对象都转化为二进制的形式存放,这个过程称为pickling,从二进制转换回对象的过程称为unpickling

    pickling过程

    >>> import pickle
    >>> my_list = [123,3,14,'小甲鱼',['another list']]

    >>> pickle_file = open('D:\\python3.3.2\小甲鱼python\python程序\第三十节课\my_list.pkl','wb')  #二进制写形式打开文件
    >>> pickle.dump(my_list,pickle_file)
    >>> pickle_file.close()

    unpickling过程       

    >>> import pickle
    >>> pickle_file = open('D:\\python3.3.2\小甲鱼python\python程序\第三十节课\my_list.pkl','rb')#以二进制读形式打开文件
    >>> my_list = pickle.load(pickle_file)
    >>> print(my_list)
    [123, 3, 14, '小甲鱼', ['another list']]

    实例:城市天气打包

    >>> pickle_file = open('D:\\python3.3.2\小甲鱼python\python程序\第三十一节课\city_data.pkl','wb')
    >>> pickle.dump(city,pickle_file)
    >>> pickle_file.close()

    032 异常处理:你不可能总是对的

    实例1:

    file_name = input('请输入需要打开的文件名:')
    file = open(file_name)
    print('文件的内容是:')
    for each_line in file:
        print(each_line)
    file.close()

    注:py文件与要打开的文件在同一个文件下则不需要加路径

    Python标准异常总结

    以下是 Python 内置异常类的层次结构:

            

    033 异常处理:你不可能总是对的2

    try-except语句

    try:

      检测范围

    except Exception[as reason]:

      出现异常(Exception)后的处理代码

    实例1:

    try:
        f = open('TE.txt')
        print(f.read())
        f.close()
    except OSError:
        print('文件打开过程中出错了!!!')

    实例2:

    try:
        f = open('TE.txt')
        print(f.read())
        f.close()
    except OSError as reason:
        print('文件打开出错原因是:\n' + str(reason))

    实例3:

    try:
        sum = 1 + '1'
        f = open('TE.txt')
        print(f.read())
        f.close()
    except OSError as reason:
        print('文件打开出错原因是:\n' + str(reason))
    except TypeError as reason:
        print('类型出错原因是:\n' + str(reason))

    实例4(多个异常统一处理):

    try:
        sum = 1 + '1'
        f = open('TE.txt')
        print(f.read())
        f.close()
    except(OSError, TypeError):
        print('出错了')

    注:try语句一旦检测到异常,剩下的语句将不会被执行

    try-finally语句

    try:

      检测范围

    except Exception[as reason]:

      出现异常(Exception)后的处理代码

    finally:

      无论如何都会被执行的代码

    实例5:

    try:
        f = open('test.txt')
        print(f.read())
        sum = 1 + '1'
    except (OSError,TypeError)as reason:
        print('出错了\n原因是:' + str(reason))
    finally:
        f.close()

    raise语句可以自己抛出一个异常

    034 丰富的else语句及简洁的with语句

    丰富的else语句

    要么怎样,要么不怎样

    if 条件:
        条件为真执行
    else:
        条件为假执行
          

    干完了能怎样,干不完就别想怎样

    实例1:

    def showMaxFactor(num):
        count = num // 2#//为整除,判断是素数,只需依次判断当前数num除以1到(num // 2)都不能整除即可
        while count > 1:
            if num % count == 0:#判断是否整除
                print('%d最大的约数是%d' % (num, count))
                break#跳出循环后else并不执行
            count -= 1
        else:#当while循环不成立时,或者理解为while循环完全被执行完了,没有给中途跳出(即break)
            print('%d是素数!' % num)

    num = int(input('请输入一个数:'))
    showMaxFactor(num)

    注:else与for语句搭配与while语句相同

    没有问题?那就干

    只要try语句块里没有出现任何异常,那么就会执行else语句块里的内容啦

    实例2:

    try:#尝试运行以下程序
        print(int('abc'))
    except ValueError as reason:#如果程序有异常时
        print('出错了:' + str(reason))
    else:#程序无异常时
        print('没有任何异常!')

    实例3:

    try:
        print(int('123'))
    except ValueError as reason:
        print('出错了:' + str(reason))
    else:
        print('没有任何异常!')

    简洁的with语句(with会自动帮你关闭文件)

    实例4:

    try:
        with open('test.txt','w') as f:
            for each_line in f:
                print(each_line)
    except (OSError,TypeError) as reason:
        print('出错了\n原因是:' + str(reason))

    035 图形用户界面入门:EasyGui

    图形用户界面编程,也就是平时常说的GUI(Graphical User  Interface),python有一个非常简单的GUI工具包:EasyGui

    GUI的安装

    导入方法一:

    >>> import easygui         #导入EasyGui
    >>> easygui.msgbox('嗨,亦我飞也')

    导入方法二:

    >>> from easygui import *
    >>> msgbox('嗨,亦我飞也')

    导入方法三(推荐使用):

    >>> import easygui as g
    >>> g.msgbox('嗨,亦我飞也')

    显示图片(注:图片需要为GIF格式,且存放在python.exe通目录

    >>> easygui.buttonbox(msg='你喜欢以下哪种水果',title='亦我飞也',choices=('草莓','西瓜','芒果'),image='aa.gif')

    实例1:

    import easygui as g
    import sys
     
    while 1:
        g.msgbox("嗨,欢迎进入第一个界面小游戏")
        msg = "请问你希望在鱼C工作室学习到什么知识呢"
        title="小游戏互动"
        choices=["谈恋爱","编程","OOXX","琴棋书画"]
        choice=g.choicebox(msg,title,choices)
     
        #note that we convert choice to string,in case
        #the user cancelled the choice,and we got None
        g.msgbox("你的选择是:"+str(choice),"结果")
        msg="你希望重新开始小游戏吗?"
        title=" 请选择"
        if g.ccbox(msg,title):  #show a Contiue/Cancel dialog
            pass #user chose Contonue
        else:
            sys.exit(0)  #user chose Cancel

    修改窗口大小(choicebox)

    修改文字大小(PROPORTIONAL_FONT)

    036 类和对象:给大家介绍对象

    给大家介绍对象

    把乱七八糟的数据扔进列表里,称数据层面的封装

    把常用的代码段打包成一个函数,称语句层面的封装

    把数据和代码都封装在一起,称对象层面的封装

    对象 = 属性 + 方法

    对象可以从静态(属性)动态(方法)两个特征来描述

    OO(面向对象)的特征

    继承

    class Turtle: # Python 中的类名约定以大写字母开头
        """关于类的一个简单例子"""
        # 属性
        color = 'green'
        weight = 10
        legs = 4
        shell = True
        mouth = '大嘴'

        # 方法
        def climb(self):
            print("我正在很努力的向前爬......")

        def run(self):
            print("我正在飞快的向前跑......")

        def bite(self):
            print("咬死你咬死你!!")

        def eat(self):
            print("有得吃,真满足^_^")

        def sleep(self):
            print("困了,睡了,晚安,Zzzz")

    调用类中的方法:

    >>> tt = Turtle()     #声明tt对象继承Turtle()
    >>> tt.climb()
    我正在很努力的向前爬......
    >>> tt.bite()
    咬死你咬死你!!

    定义一个带列表类MyList,将list2对象继承于它,则列表的功能继承它的对象都可以使用

    >>> class MyList(list):
        pass

    >>> list2 = MyList()

    >>> list2.append(5)
    >>> list2.append(6)

    >>> list2.append(1)
    >>> list2
    [5, 6, 1]
    >>> list2.sort()
    >>> list2
    [1, 5, 6]

    多态(下例中都调用的名字相同的方法,但实现不一样)

    >>> class A:
        def fun(self):
            print('我是小A。。。')

            
    >>> class B:
        def fun(self):
            print('我是小B。。。')

            
    >>> a = A()
    >>> b = B()
    >>> a.fun()
    我是小A。。。
    >>> b.fun()
    我是小B。。。

    037 类和对象:面向对象编程

    self是什么?

    Python的self其实就相当于C++的this指针。由同一个类可以生产无数对象,当一个对象的方法被调用的时候,对象会将自身的引用作为第一个参数传给该方法,那么python就知道需要操作哪个对象的方法了。

    >>> class Ball:
        def setName(self,name):
            self.name = name
        def kick(self):
            print('我叫%s,该死的,谁踢我。。。' % self.name)

            
    >>> a = Ball()

    >>> a.setName('球A')
    >>> b = Ball()

    >>> b.setName('球B')

    >>> a.kick()
    我叫球A,该死的,谁踢我。。。
    >>> b.kick()
    我叫球B,该死的,谁踢我。。。

    你听说过Python的魔法方法吗?

    python的这些具有魔法的方法,总是被双下划线所包围,例如__init__(),即构造方法,也称构造函数,这个方法会在对象被创建时自动调用。其实,实例化对象时是可以传入参数的,这些参数会自动传入__init__()方法中,可以通过重写这个方法来自定义对象的初始化操作

    实例:

    >>> class Ball():
        def __init__(self,name):
            self.name = name
        def kick(self):
            print('我叫%s,该死的,谁踢我。。。' % self.name)

            
    >>> b = Ball('小土豆')
    >>> b.kick()
    我叫小土豆,该死的,谁踢我。。。

    公有和私有?python内部采用了一种叫 name mangling(名字改编)的技术

    默认上对象的属性和方法都是公开的,可以直接通过点操作符(.)进行访问:

    >>> class Person:
        name = '亦我飞也'

        
    >>> p = Person()
    >>> p.name
    '亦我飞也'

    为了实现定义私有变量,只需要在变量名或函数名前加上"__"两个下划线,那么这个函数或变量就会变成私有的了:

    私有变量不可以直接由外部访问

    >>> class Person:
        __name = '亦我飞也'

        
    >>> p = Person()
    >>> p.__name
    Traceback (most recent call last):
      File "<pyshell#65>", line 1, in <module>
        p.__name
    AttributeError: 'Person' object has no attribute '__name'

    室友变量可以由内部(内部函数)进行访问

    >>> class Person:
        __name = '亦我飞也'
        def getName(self):
            return self.__name

        
    >>> p = Person()
    >>> p.__name
    Traceback (most recent call last):
      File "<pyshell#72>", line 1, in <module>
        p.__name
    AttributeError: 'Person' object has no attribute '__name'

    >>> p.getName()
    '亦我飞也'

    其实,name mangling(名字改编)技术,只是把双下划线开头的变量进行了改名而已。实际上在外部使用“_类名__变量名“即可访问双下划线开头的私有变量了

    >>> p._Person__name
    '亦我飞也'

    038 类和对象:继承

    继承

                      子类                              父类

    class DerivedClassName(BaseClassName):

    ……

    实例:一个子类可以继承它的父类的所有属性和方法

    >>> class Parent:
        def hello(self):
            print('正在调用父类的方法。。。')

            

    >>> class Child(Parent):    #子类继承父类
        pass     #直接往下执行

    >>> p = Parent()
    >>> p.hello()
    正在调用父类的方法。。。
    >>> c = Child()
    >>> c.hello()
    正在调用父类的方法。。。

    如果子类中定义与父类同名的方法或属性,则会自动覆盖父类对应的方法和属性(即子类方法属性改变,父类是不变的)

    >>> class Child(Parent):
        def hello(self):
            print('正在调用子类的方法')

            
    >>> c = Child()
    >>> c.hello()
    正在调用子类的方法
    >>> p.hello()
    正在调用父类的方法。。。

    实例2:

    import random as r
    class Fish:
        def __init__(self):
            self.x = r.randint(0,10)
            self.y = r.randint(0,10)

        def move(self):
            self.x -= 1
            print('我的位置是:',self.x,self.y)


    class Goldfish(Fish):
        pass

    class Garp(Fish):
        pass

    class Shark(Fish):
        def __init__(self):
            self.hungry = True

        def eat(self):
            if self.hungry:
                print('吃货的梦想就是天天有的吃')
                self.hungry = False
            else:
                print('太撑了,吃不下了!')

    >>> fish = Fish()
    >>> fish.move()
    我的位置是: -1 10
    >>> fish.move()
    我的位置是: -2 10
    >>> goldfish = Goldfish()
    >>> goldfish.move()
    我的位置是: 2 3
    >>> goldfish.move()
    我的位置是: 1 3
    >>> shark = Shark()
    >>> shark.eat()
    吃货的梦想就是天天有的吃
    >>> shark.eat()
    太撑了,吃不下了!
    >>> shark.move()    #报错原因时因为子类重写构造函数,覆盖了父类D的构造函数
    Traceback (most recent call last):
      File "<pyshell#9>", line 1, in <module>
        shark.move()
      File "D:\python3.3.2\小甲鱼python\python程序\第三十八节课\fish.py", line 8, in move
        self.x -= 1
    AttributeError: 'Shark' object has no attribute 'x'

    注:继承父类属性的子类,其变量值只属于当前子类,是子类的局部变量

    报错修改部分解决方法一:调用未绑定的父类方法

    >>> shark = Shark()
    >>> shark.move()
    我的位置是: 2 1
    >>> shark.move()
    我的位置是: 1 1

    报错修改部分解决方法二:使用super函数super函数会帮我们自动找到基类的方法,而且还自动为我们传入self参数

    >>> shark = Shark()
    >>> shark.move()
    我的位置是: 1 1
    >>> shark.move()
    我的位置是: 0 1

    多重继承

    class DerivedClassName(Base1, Base2, Base3):

    ……

    实例:子类c同时继承基类Base1和基类Base2

    >>> class Base1:
        def fool1(self):
            print('我是fool1,我为Base1代言。。。')

            
    >>> class Base2:
        def fool2(self):
            print('我是fool2,我为Base2代言。。。')

            
    >>> class C(Base1,Base2):
        pass

    >>> c = C()
    >>> c.fool1()
    我是fool1,我为Base1代言。。。
    >>> c.fool2()
    我是fool2,我为Base2代言。。。

    039 类和对象:拾遗

    组合(将需要的类一起进行实例化并放入新的类中)

    实例:

    class Turtle:
        def __init__(self,x):
            self.num = x

    class Fish:
        def __init__(self,x):
            self.num = x

    class Pool:
        def __init__(self,x,y):
            self.turtle = Turtle(x)
            self.fish = Fish(y)

        def print_num(self):
            print('水池里一共有乌龟 %d 条,鱼 %d 条' % (self.turtle.num,self.fish.num))

    >>> pool = Pool(5,2)
    >>> pool.print_num()
    水池里一共有乌龟 5 条,鱼 2 条

    现在要求定义一个类,叫水池,水池里要有乌龟和鱼。

    类、类对象和实例对象

    以下例子可见,对实例对象c的count属性赋值后,就相当于覆盖了类对象C的count属性。如果没有赋值覆盖,那么引用的是类对象的count属性

    >>> a = C()
    >>> b = C()
    >>> c = C()
    >>> print(a.count,b.count,c.count)
    0 0 0
    >>> c.count += 10
    >>> print(a.count,b.count,c.count)
    0 0 10
    >>> C.count += 100
    >>> print(a.count,b.count,c.count)
    100 100 10

    另外,如果属性的名字跟方法名相同,属性会覆盖方法:

    >>> class C:
        def x(self):
            print('X-man')

            
    >>> c = C()
    >>> c.x()
    X-man
    >>> c.x = 1              #新定义对象c的一个x属性,并赋值为1
    >>> c.x
    1
    >>> c.x()     #可见,方法x()已经被属性x给覆盖了
    Traceback (most recent call last):
      File "<pyshell#8>", line 1, in <module>
        c.x()
    TypeError: 'int' object is not callable

    结论:不要试图在一个类里边定义出所有能想到的特性和方法,应该利用继承和组合机制来进行扩展;用不同的词性命名,如属性名用名词、方法名用动词,并使用骆驼命名法等。

    到底什么是绑定?

    实例1:(python严格要求需要有实例才能被调用,即绑定概念)

    >>> class BB:
        def printBB():        #缺少self,导致无法绑定具体对象
            print('no zuo no die')

            
    >>> BB.printBB()
    no zuo no die
    >>> bb = BB()
    >>> bb.printBB()        #出现错误原因是由于绑定机制,自动把bb对象作为第一个参数传入
    Traceback (most recent call last):
      File "<pyshell#15>", line 1, in <module>
        bb.printBB()
    TypeError: printBB() takes 0 positional arguments but 1 was given

     

    Python严格要求方法需要有实例才能被调用,这种限制其实就是Python所谓的绑定概念。

    040 类和对象:一些相关的BIF

    一些相关的BIF

    issubclass(class, classinfo)  如果第一个参数(class)是第二个参数(classinfo)的一个子类,则返回True,否则返回False

    >>> class A:
        pass

    >>> class B(A):
        pass

    >>> issubclass(B,A)
    True
    >>> issubclass(B,B)   #一个类被认为是其自身的子类
    True
    >>> issubclass(B,object)      # object是所有类的基类
    True
    >>> class C:
        pass

    >>> issubclass(B,C)
    False

    isinstance(object, classinfo)  如果第一个参数(object)是第二个参数(classinfo)的实例对象,则返回True,否则返回False

    >>> issubclass(B,C)       注:第一个参数如果不是对象,则永远返回False
    False
    >>> b1 = B()
    >>> isinstance(b1,B)
    True
    >>> isinstance(b1,C)
    False
    >>> isinstance(b1,A)
    True
    >>> isinstance(b1,(A,B,C))
    True

    hasattr(object, name)  用来测试一个对象里是否有指定的属性,第一个参数(object)是对象,第二个参数(name)是属性名(属性的字符串名字)

    >>> class C:
        def __init__(self,x=0):
            self.x = x

            
    >>> c1 = C()
    >>> hasattr(c1,'x')    
    #注意,属性名要用引号括起来
    True

     

    getattr(object, name[, default])  返回对象指定的属性值,如果指定的属性不存在,则返回default(可选参数);若没有设置default参数,则抛出异常

    >>> getattr(c1,'x')
    0
    >>> getattr(c1,'y')

    Traceback (most recent call last):
      File "<pyshell#25>", line 1, in <module>
        getattr(c1,'y')
    AttributeError: 'C' object has no attribute 'y'

    setattr(object, name, value)  可以设置对象中指定属性的值,如果指定的属性不存在,则会新建属性并赋值
    >>> setattr(c1,'y','FishC')
    >>> getattr(c1,'y')
    'FishC'

    delattr(object, name)  用于删除对象中指定的属性,如果属性不存在,抛出异常。

    >>> delattr(c1,'y')
    >>> delattr(c1,'Z')

    Traceback (most recent call last):
      File "<pyshell#30>", line 1, in <module>
        delattr(c1,'Z')
    AttributeError: Z

    property(fget=None, fset=None, fdel=None, doc=None)  用来通过属性设置属性,第一个参数是获取属性的方法名,第二个参数是设置属性的方法名,第三个参数是删除属性的方法名

    >>> class C:
        def __init__(self,size =10):
            self.size = size
        def getSize(self):
            return self.size
        def setSize(self,value):
            self.size = value
        def delSize(self):
            del self.size
        x=property(getSize,setSize,delSize)

        

    >>> c = C()
    >>> c.x         #调用getSize()
    10
    >>> c.x = 12      #调用SetSize()
    >>> c.x
    12
    >>> c.size
    12
    >>> del c.x      #调用DelSize()
    >>> c.size
    Traceback (most recent call last):
      File "<pyshell#53>", line 1, in <module>
        c.size
    AttributeError: 'C' object has no attribute 'size'

    041 魔法方法:构造和析构

    __init__(self[, ...]) 方法是类在实例化成对象的时候首先会调用的一个方法

    >>> class Rectangle:
        def __init__(self,x,y):
            self.x = x
            self.y = y
        def getPeri(self):
            return (self.x + self.y) * 2
        def getArea(self):
            return self.x * self.y

    >>> rect = Rectangle(5,2)
    >>> rect.getPeri()
    14
    >>> rect.getArea()
    10

       注:__init__()方法的返回值一定是None 

    其实,__new__()才是在一个对象实例化时候所调用的第一个方法,它的第一个参数是这个类(cla),而其他的参数会直接传递给__init__()方法

    __new__(cls[, ...])

    >>> class CapStr(str):
        def __new__(cls,string):
            string = string.upper()
            return str.__new__(cls,string)

        
    >>> a = CapStr('hello world')
    >>> a
    'HELLO WORLD

    __del__(self)  当对象将要被销毁的时候,这个方法就会被调用。但要注意,并非del x就相当于调用x.__del__(),__del__()方法是当垃圾回收机制回收这个对象的时候才调用的。

    >>> class C:
        def __init__(self):
            print('我是__init__方法,我被调用了...')
        def __del__(self):
            print('我是__del__方法,我被调用l...')

            
    >>> c1 = C()     #创建对象c1
    我是__init__方法,我被调用了...
    >>> c2 = c1
    >>> c3 = c2
    >>> del c1
    >>> del c2
    >>> del c3   #删除c3时,对象c1才会彻底被删除(即没有标签指向对象c1时,其才会被回收)
    我是__del__方法,我被调用l...

    042 魔法方法:算术运算

    python2.2以后,对类和类型进行了统一,做法就是讲int()、float()、str()、list()、tuple()这些BIF转换为工厂函数(类对象):

    >>> type(len)
    <class 'builtin_function_or_method'>            #普通的BIF
    >>> type(int)
    <class 'type'>             #工厂函数(类对象),当调用它们的时候,其实就是创建了一个相应的实例对象
    >>> type(dir)
    <class 'builtin_function_or_method'>
    >>> type(list)
    <class 'type'>

    >>> a = int('123')        #创建一个相应的实例对象a
    >>> b = int('345')
    >>> a + b              #python在两个对象进行相加操作
    468

    举个例子,下面定义一个比较特立独行的类:

    >>> class New_int(int):
        def __add__(self,other):
            return int.__sub__(self,other)
        def __sub__(self,other):
            return int.__add__(self,other)

        
    >>> a = New_int(3)
    >>> b = New_int(5)
    >>> a + b    #两个对象相加,触发 __add__(self,other)方法
    -2
    >>> a - b
    8
    >>>

    实例2:

    >>> class New_int(int):
        def __add__(self,other):
            return (int(self) + int(other))       #将self与other强制转换为整型,所以不会出现两个对象相加触发__add__()方法
        def __sub__(self,other):
            return (int(self) - int(other))

        
    >>> a = New_int(3)
    >>> b = New_int(5)
    >>> a + b
    8

    043 魔法方法:算术运算2

    实例1:

    >>> class int(int):
        def __add__(self,other):
            return int.__sub__(self,other)

        
    >>> a = int(3)
    >>> b = int(2)
    >>> a + b
    1

    反运算:

    反运算与算术运算符的不同之处是,反运算多了一个'r',例如 __add__()的反运算对应为 __radd__()

    >>> a + b

    这里a是加数,b是被加数,如果a对象的__add__()方法没有实现或者不支持相应的操作,那么python就会自动调用b的__radd__()方法

    实例:

    >>> class Nint(int):
        def __radd__(self,other):
            return int.__sub__(self,other)

        
    >>> a = Nint(5)
    >>> b = Nint(3)
    >>> a + b      #由于a对象默认有__add__()方法,所以b的__radd__()没有执行
    8

    实例2:

    >>> class Nint(int):
        def __radd__(self,other):
            return int.__sub__(self,other)

        
    >>> b = Nint(5)
    >>> 3 + b         #由于3无__add__()方法,所以执行b的反运算__radd__(self,other)方法,其中self是b对象
    2

    注:在重写反运算魔法方法时,一定要注意顺序问题。

    增量赋值运算:

    比较操作符:

    其它操作符:

    044 魔法方法:简单定制

    简单定制

    基本要求:

    定制一个计时器的类

    startstop方法代表启动计时和停止计时

    假设计时器对象t1print(t1)和直接调用t1均显示结果

    当计时器未启动或已经停止计时,调用stop方法会给予温馨的提示

    两个计时器对象可以进行相加:t1 + t2

    只能使用提供的有限资源完成

    你需要这些资源

    使用time模块的localtime方法获取时间

    扩展阅读:time 模块详解(时间获取和转换)

    有关time模块的localtime方法获取时间(参考:

    https://fishc.com.cn/forum.php?mod=viewthread&tid=51326&extra=page%3D1%26filter%3Dtypeid%26typeid%3D403

    time.localtime返回struct_time的时间格式

    表现你的类:__str__ __repr__

    实例:

    import time as t   #导入时间模块,调用对象t

    class Mytimer():
        def __init__(self):
            self.unit = ['年','月','天','小时','分钟','秒']
            self.prompt = "未开始计时"
            self.lasted = []
            self.begin = 0  #属性
            self.end = 0
        def __str__(self):
            return self.prompt

        __repr__ = __str__

        def __add__(self,other):   #重写加法操作符,运行时间相加
            prompt = "总共运行了"
            result = []
            for index in range(6):
                result.append(self.lasted[index] + other.lasted[index])
                if result[index]:
                    prompt += (str(result[index]) + self.unit[index])
            return prompt
                               
        #开始计时
        def start(self):    #方法,属性名和方法名不能相同
            if not self.stop:
                self.prompt = ("提示:请先调用stop()停止计时!")
            else:
                self.begin = t.localtime()
                print('计时开始...')

        #停止计时
        def stop(self):
            if not self.begin:
                print('提示:请先调用start()进行计时!')
            else:
                self.end = t.localtime()
                self._calc()
                print('计时结束!')

        #内部方法,计算运行时间
        def _calc(self):
            self.prompt = "总共运行了"
            for index in range(6):
                self.lasted.append(self.end[index] - self.begin[index])
                if self.lasted[index]:
                    self.prompt += (str(self.lasted[index]) + self.unit[index])
            #为下一轮计时初始化变量
            self.begin = 0
            self.end = 0

    >>> t1 = Mytimer()
    >>> t1.stop()
    提示:请先调用start()进行计时!
    >>> t1.start()
    计时开始...
    >>> t1.stop()
    计时结束!
    >>> t1
    总共运行了4秒
    >>> t2 = Mytimer()
    >>> t2.start()
    计时开始...
    >>> t2.stop()
    计时结束!
    >>> t2
    总共运行了4秒
    >>> t1 + t2
    '总共运行了8秒'        

    进阶定制

    如果开始计时的时间是202222216:30:30,停止时间是202512315:30:30,那按照我们用停止时间减开始时间的计算方式就会出现负数3-11-1小时)你应该对此做一些转换

    现在的计算机速度都非常快,而我们这个程序最小的计算单位却只是秒,精度是远远不够的

    045 魔法方法:属性访问

    属性访问

    __getattr__(self, name)

    定义当用户试图获取一个不存在的属性时的行为

    __getattribute__(self, name)

    定义当该类的属性被访问时的行为

    __setattr__(self, name, value)

    定义当一个属性被设置时的行为

    __delattr__(self, name)

    定义当一个属性被删除时的行为

    实例1:

    class C:
        def __getattribute__(self, name):
            print('getattribute')
            # 使用 super() 调用 object 基类的 __getattribute__ 方法
            return super().__getattribute__(name)

        def __setattr__(self, name, value):
            print('setattr')
            super().__setattr__(name, value)

        def __delattr__(self, name):
            print('delattr')
            super().__delattr__(name)

        def __getattr__(self, name):
            print('getattr')

    >>> c = C()
    >>> c.x
    getattribute
    getattr
    >>> c.x = 1
    setattr
    >>> c.x
    getattribute
    1
    >>> del c.x
    delattr
    >>> setattr(c,'y','Yellow')
    setattr

    练习要求

    写一个矩形类,默认宽和高两个属性

    如果为一个叫square的属性赋值,那么说明这是一个正方形,值就是正方形的边长,此时宽和高都应该等于边长。

    实例2:

    class Rectangle:
        def __init__(self, width=0, height=0):
            self.width = width
            self.height = height

        def __setattr__(self, name, value):#一发生赋值操作,则会触发__setattr__()魔法方法
            if name == 'square':#判断name属性是否为正方形
                self.width = value
                self.height = value
            else:
                self.__dict__[name] = value

        def getArea(self):
            return self.width * self.height

    >>> r1 = Rectangle(4,5)
    >>> r1.getArea()
    20
    >>> r1.square = 10
    >>> r1.getArea()
    100

    046 魔法方法:描述符(Property的原理)

    描述符

    描述符就是将某种特殊类型的类的实例指派给另一个类的属性。

    __get__(self, instance, owner)

    用于访问属性,它返回属性的值

    __set__(self, instance, value)

    将在属性分配操作中调用,不返回任何内容

    __delete__(self, instance)

    控制删除操作,不返回任何内容

    实例:

    >>> class MyDecriptor:
        def __get__(self,instance,owner):
            print("getting...",self,instance,owner)
        def __set__(self,instance,value):
            print("setting...",self,instance,value)
        def __delete__(self,instance):
            print("deleting...",self,instance)

     

    >>> class Test:
        x = MyDecriptor()   #取Mydecriptor类的实例指派给Test类的属性x

    >>> test = Test()
    >>> test.x
    getting... <__main__.MyDecriptor object at 0x00000000033467F0> <__main__.Test object at 0x000000000335EF98> <class '__main__.Test'>
    >>> test
    <__main__.Test object at 0x000000000335EF98>
    >>> test.x = "X-man"
    setting... <__main__.MyDecriptor object at 0x00000000033467F0> <__main__.Test object at 0x000000000335EF98> X-man
    >>> del test.x
    deleting... <__main__.MyDecriptor object at 0x00000000033467F0> <__main__.Test object at 0x000000000335EF98>

     

    实例2:

    >>> class MyProperty:
        def __init__(self,fget = None,fset = None,fdel = None):
            self.fget = fget
            self.fset = fset
            self.fdel = fdel
        def __get__(self,instance,owner):
            return self.fget(instance)
        def __set__(self,instance,value):
            self.fset(instance,value)
        def __delete__(self,instance):
            self.fdel(instance)

            
    >>> class C:
        def __init__(self):
            self._x = None
        def getX(self):
            return self._x
        def setX(self,value):
            self._x = value
        def delX(self):
            del self._x
        x = MyProperty(getX,setX,delX)

        
    >>> c = C()
    >>> c.x = "HELLOW"
    >>> c.x
    'HELLOW'
    >>> c._x
    'HELLOW'
    >>> del c.x
    >>> c._x
    Traceback (most recent call last):
      File "<pyshell#70>", line 1, in <module>
        c._x
    AttributeError: 'C' object has no attribute '_x'

    练习要求

    先定义一个温度类,然后定义两个描述符类用于描述摄氏度和华氏度两个属性

    要求个属性会自动进行转换,也就是说你可以给摄氏度这个属性赋值,然后打印的华氏度属性是自动转换后的结果。

    实例3:

    ss Celsius:  #摄氏度描述符类
        def __init__(self,value = 26.0):#self为描述符类自身(此为摄氏度描述符类)的实例(此为cel)
            self.value = float(value)
        def __get__(self,instance,owner):#instance是这个描述符的拥有者所在的类的实例(此为temp)
            return self.value
        def __set__(self,instance,value):#owner是这个描述符的拥有者所在的类本身(此为温度类)
            self.value = float(value)

    class Fahrenheit:   #华氏度描述符类
        def __get__(self,instance,owner):
            return instance.cel * 1.8 +32  #摄氏度转华氏度
        def __set__(self,instance,value):
            instance.cel = ((float)(value)- 32)/ 1.8   #华氏度转摄氏度
            
    class Temperature:   #温度类
        cel = Celsius()   #设置摄氏度属性(描述符类的实例指派给了温度类的属性)
        fah = Fahrenheit()#设置华氏度属性

    >>> temp = Temperature()
    >>> temp.cel
    26.0
    >>> temp.fah
    78.80000000000001
    >>> temp.fah = 78.8
    >>> temp.cel
    25.999999999999996

    047 魔法方法:定制序列

    协议是什么?

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

    容器类型的协议

    如果说你希望定制的容器是不可变的话,你只需要定义__len__()__getitem__()方法。

    如果你希望定制的容器是可变的话,除了__len__()__getitem__()方法,你还需要定义__setitem__()__delitem__()两个方法。

    练习要求

    编写一个不可改变的自定义列表,要求记录列表中每个元素被访问的次数。

    class CountList:  #定义记录列表中每个元素访问次数类
        def __init__(self,*args): #参数是可变类型的
            self.values = [x for x in args]#将args的数据存入列表self.values中
            self.count = {}.fromkeys(range(len(self.values)),0)#创建字典,初试化为0

        def __len__(self):  #返回容器中元素的个数
            return len(self.values)#len方法用于返回参数的长度 
        def __getitem__(self,key):  #获取容器中指定元素的行为,key为访问对应的键
            self.count[key] += 1#每访问一次,字典键对应的键值加1
            return self.values[key]

    >>> c1 = CountList(1,3,5,7,9)
    >>> c2 = CountList(2,4,6,8,10)
    >>> c1[1]  #c1[1]第一次访问
    3
    >>> c2[2]
    6
    >>> c1[1] + c2[2] #c1[1]第二次访问
    9
    >>> c1.count
    {0: 0, 1: 2, 2: 0, 3: 0, 4: 0}
    >>> c2.count
    {0: 0, 1: 0, 2: 2, 3: 0, 4: 0}

    048 魔法方法:迭代器

    迭代的意思类似于循环,每一次重复的过程被称为一次迭代的过程,而每一次迭代得到的结果会被用来作为下一次迭代的初始值。提供迭代方法的容器称为迭代器(如序列(列表、元组、字符串)、字典等)。

    对一个容器对象调用iter()就得到它的迭代器,调用next()迭代器就会返回下一个值。入托迭代器没有值可以返回了,就会抛出异常。

    •iter()

    –__iter__()

    •next()

    –__next__()

    实例1:

    >>> string = "FishC"
    >>> it = iter(string)
    >>> next(it)
    'F'
    >>> next(it)
    'i'
    >>> next(it)
    's'
    >>> next(it)
    'h'
    >>> next(it)
    'C'
    >>> next(it)
    Traceback (most recent call last):
      File "<pyshell#8>", line 1, in <module>
        next(it)
    StopIteration

    一个容器如果是迭代器,那就必须实现__iter__()魔法方法,这个方法实际上就是返回迭代器本身。重点要实现的是__next__()魔法方法,因为它决定了迭代的规则。

    实例2:

    >>> class Fibs:
        def __init__(self):
            self.a = 0
            self.b = 1
        def __iter__(self):
            return self
        def __next__(self):
            self.a,self.b = self.b,self.a + self.b
            return self.a

        
    >>> fibs = Fibs()
    >>> for each in fibs:
        if each < 20:
            print(each)
        else:
            break

        
    1
    1
    2
    3
    5
    8
    13

    实例3:

     

    >>> class Fibs:
        def __init__(self,n =20):
            self.a = 0
            self.b = 1
            self.n = n
        def __iter__(self):
            return self
        
        def __next__(self):
            self.a,self.b = self.b,self.a + self.b
            if self.a > self.n:
                raise StopIteration
            return self.a

        
    >>> fibs = Fibs()
    >>> for each in fibs:
        print(each)

        
    1
    1
    2
    3
    5
    8
    13

     

    >>> fibs = Fibs(10)
    >>> for each in fibs:
        print(each)

        
    1
    1
    2
    3
    5
    8

     

    049 乱入:生成器

    所谓协同程序,就是可以运行的独立函数调用,函数可以暂停或者挂起,并在需要的时候从程序离开的地方继续或者重新开始。

    生成器可以暂时挂起函数,并保留函数的局部变量等数据,然后在再次调用它的时候,从上次暂停的位置继续执行下去。

    一个函数中如果有yield语句,则被定义为生成器。

    实例1:

    >>> def myGen():
        print("生成器被执行了!")
        yield 1   #暂停一次,相当于return,返回1
        yield 2     #暂停一次,相当于return,返回2

        
    >>> myG = myGen()
    >>> next(myG)
    生成器被执行了!
    1
    >>> next(myG)
    2

    像前面介绍的斐波那契的例子,也可以用生成器来实现:

    >>> def fibs():
        a = 0
        b = 1
        while True:
            a,b = b,a + b
            yield a

            
    >>> for each in fibs():
        if each > 100:
            break
        print(each)

        
    1
    1
    2
    3
    5
    8
    13
    21
    34
    55
    89

    列表推导式表达:

    100以内,能被2整除,但不能被3整除的所有整数

    >>> a = [i for i in range(100) if not (i % 2) and (i % 3 )]
    >>> a
    [2, 4, 8, 10, 14, 16, 20, 22, 26, 28, 32, 34, 38, 40, 44, 46, 50, 52, 56, 58, 62, 64, 68, 70, 74, 76, 80, 82, 86, 88, 92, 94, 98]

    字典推导式:

    10以内是否为偶数

    >>> a = {i:i % 2 == 0 for i in range(10)}
    >>> a
    {0: True, 1: False, 2: True, 3: False, 4: True, 5: False, 6: True, 7: False, 8: True, 9: False}

    集合推导式:

    >>> a = {i for i in [1,2,3,3,4,5,5,5,6,7,7,8]}
    >>> a
    {1, 2, 3, 4, 5, 6, 7, 8}

    元组生成器推导式:

    >>> e = (i for i in range(5))
    >>> next(e)
    0
    >>> next(e)
    1
    >>> next(e)
    2

    050 模块:模块就是程序

    什么是模块

    容器 -> 数据封装

    函数 -> 语句封装

    -> 方法和属性的封装

    模块 -> 模块就是程序

    命名空间

    爱的宣言:世界上只有一个名字,使我这样牵肠挂肚,像有一根看不见的线,一头牢牢系在我心尖上,一头攥在你手中,这个名字就叫做鱼C工作室计算机一班的小花……

    导入模块

    第一种:import 模块名

    实例1:import导入模块

    实例2:import导入模块

    第二种:from 模块名 import 函数名(不推荐使用)

    第三种:import 模块名 as 名字(推荐使用)

    TemperatureConversion文件:

    def c2f(cal):
        return cal * 1.8 + 32
    def f2c(fah):
        return (fah - 32)/1.8

    calc文件:

    import TemperatureConversion as tc  #tc为取得新名字

    print("32摄氏度 = %.2f 华氏度\n" % tc.c2f(32))
    print("99华氏度 = %.2f 摄氏度" % tc.f2c(99))

    051 模块:__name__='__main__'、搜索路径和包

    模块!模块!

    实例1:为TemperatureConversion添加测试程序(TemperatureConversion被作为程序运行)

    def c2f(cal):
        return cal * 1.8 + 32

    def f2c(fah):
        return (fah - 32)/1.8

    def test():
        print("0摄氏度 = %.2f 华氏度\n" % c2f(0))
        print("0华氏度 = %.2f 摄氏度" % f2c(0))

    test()

    运行calc文

    当希望TemperatureConversion被调用时作为模块导入时

    def c2f(cal):
        return cal * 1.8 + 32

    def f2c(fah):
        return (fah - 32)/1.8

    def test():
        print("0摄氏度 = %.2f 华氏度" % c2f(0))
        print("0华氏度 = %.2f 摄氏度" % f2c(0))

    if __name__ == "__main__":#当此文件当做程序运行时,执行test(),否则不执行
        test()

    运行calc文件

    if __name__ == ‘__main__’

    搜索路径(系统会首先搜索的路径)

    >>> import sys
    >>> sys.path
    ['D:\\python3.3.2\\小甲鱼python\\python程序\\第五十节课\\Temperature', 'D:\\python3.3.2\\Lib\\idlelib', 'C:\\windows\\system32\\python33.zip', 'D:\\python3.3.2\\DLLs', 'D:\\python3.3.2\\lib', 'D:\\python3.3.2', 'D:\\python3.3.2\\lib\\site-packages']

    添加搜索路径:

    >>> import TemperatureConversion
    Traceback (most recent call last):
      File "<pyshell#0>", line 1, in <module>
        import TemperatureConversion
    ImportError: No module named 'TemperatureConversion'

    >>> import sys
    >>> sys.path.append("D:\\python3.3.2\WODE\Temperature")
    >>> sys.path
    ['', 'D:\\python3.3.2\\Lib\\idlelib', 'C:\\windows\\system32\\python33.zip', 'D:\\python3.3.2\\DLLs', 'D:\\python3.3.2\\lib', 'D:\\python3.3.2', 'D:\\python3.3.2\\lib\\site-packages', 'D:\\python3.3.2\\WODE\\Temperature']
    >>> import TemperatureConversion
    >>> TemperatureConversion.f2c(59)
    15.0

    package

    1.创建一个文件夹,用于存放相关的模块,文件夹的名字即包的名字;

    2.文件夹中创建一个__init__.py的模块文件,内容可以为空;

    3.相关的模块放入文件夹中

    052 模块:像个极客一样去思考

    使用print调用__doc__属性,可以带格式查看这个模块的简介

    使用dir()可以查询到该模块定义了哪些变量、函数和类

    053 论一只爬虫的自我修养

    Python如何访问互联网?

     

    URL的一般格式为(带方括号[]的为可选项)

    protocol :// hostname[:port] / path / [;parameters][?query]#fragment

    URL由三部分组成:

    第一部分是协议httphttpsftpfileed2k…

    第二部分是存放资源的服务器的域名系统或IP地址(有时候要包含端口号,各种传输协议都有默认的端口号,如http的默认端口为80)。

    第三部分是资源的具体地址,如目录文件名

    054 论一只爬虫的自我修养2:实战

    import urllib.request

    response = urllib.request.urlopen('http://placekitten.com/g/500/600')#  返回文件对象response
    cat_imag = response.read()

    with open('cat_500_600.jpg','wb') as f:
        f.write(cat_imag)

    >>> response.geturl()
    'http://placekitten.com/g/500/600'
    >>> response.info()
    <http.client.HTTPMessage object at 0x00000000034EAA20>
    >>> print(response.info())
    Date: Sat, 27 Jul 2019 02:44:18 GMT
    Content-Type: image/jpeg
    Transfer-Encoding: chunked
    Connection: close
    Set-Cookie: __cfduid=d3cd08233581619b9ef8464ae93f7d5ff1564195458; expires=Sun, 26-Jul-20 02:44:18 GMT; path=/; domain=.placekitten.com; HttpOnly
    Access-Control-Allow-Origin: *
    Cache-Control: public, max-age=86400
    Expires: Sun, 28 Jul 2019 02:44:18 GMT
    CF-Cache-Status: HIT
    Age: 66459
    Vary: Accept-Encoding
    Server: cloudflare
    CF-RAY: 4fcb454ecc35ce6b-LHR


    >>> response.getcode()
    200

    055 论一只爬虫的自我修养3:隐藏

    修改 headers

    通过Requestheaders参数修改

    通过Request.add_header()方法修改

    代理

    步骤:

    1. 参数是一个字典 {‘类型’:‘代理ip:端口号’}

    proxy_support = urllib.request.ProxyHandler({})

     

    2. 定制、创建一个 opener

    opener = urllib.request.build_opener(proxy_support)

     

    3a. 安装 opener

    urllib.request.install_opener(opener)

    3b. 调用 opener

    opener.open(url)

     

    064 GUI的终极选择:Tkinter

     

    >>> import tkinter   #Tkinter是python默认的GUI库,导入Tkinter模块
    >>> 

    实例1:

    import tkinter as tk

    root = tk.Tk()#创建一个主窗口,用于容纳整个GUI程序
    root.title("FishC Demo")#设置主窗口对象的标题栏

    #添加一个Label组件,可以显示文本、图标或者图片(此处显示文本)
    theLabel = tk.Label(root,text = "我的第二个窗口程序")
    theLabel.pack()#调用Label组件的pack方法,用于自动调节组件自身尺寸

    root.mainloop()#执行此语句后,窗口才会显示,程序进入主事件循环

    实例2:

    import tkinter as tk

    class App:#创建类App
        def __init__(self,root):#self为指向App类的指针
            #创建一个框架,然后在里面添加一个Button按钮组件,框架用来将复杂布局中按钮分组
            frame = tk.Frame(root)
            frame.pack(side = tk.RIGHT,padx = 10,pady = 10)#调节框架自身尺寸,此处设置为右对齐(右上角为原点),偏移(10,10)
            
            #创建一个按钮组件,fg(foreground),设置前景色
            #创建一个Button按钮,属性为self.hi_there,属于frame框架,按钮按下时调用self.say_hi方法
            #设置前景色为黑色,背景色为白色
            self.hi_there = tk.Button(frame,text = "打招呼",bg = "black",fg = "white",command = self.say_hi)
            self.hi_there.pack()#自动调节自身尺寸
            
            #say_hi()方法定义实现   
        def say_hi(self):
            print("互联网广大朋友们好,我是亦我飞也!")
            
            
    root = tk.Tk()#创建一个主窗口(toplever的根窗口),并把它作为参数实例化app对象,用于容纳整个GUI程序,
    app = App(root)#创建类App的一个实例对象app,传入参数为root

    app.mainloop()#执行此语句后,窗口才会显示,程序进入主事件循环
     

    065 GUI的终极选择:Tkinter2

    实例1:Label组件显示文字与gif图片

    #导入tkinter模块的所有内容
    from tkinter import *

    #创建主窗口
    root = Tk()
    #创建一个文本Label对象,文字为左对齐,离左边边框距离为10
    textLabel = Label(root,
                      text = "您下载的影片含有未成年人限制内容,\n请满18周岁后再点击观看!",
                      justify = LEFT,padx = 10)
    #Label组件为左对齐
    textLabel.pack(side = LEFT)

    #创建一个图像Label对象
    #用PhotoImage实例化一个图片对象(支持gif格式的图片)
    photo = PhotoImage(file = "18.gif")
    imgLabel = Label(root,image = photo)
    imgLabel.pack(side = RIGHT)

    mainloop()
     

    实例2:

    例2:文字显示在图片上

    #导入tkinter模块的所有内容
    from tkinter import *

    #创建主窗口
    root = Tk()

    #创建一个图像Label对象
    photo = PhotoImage(file = "bg.gif")
    #创建一个文本Label对象
    textLabel = Label(root,
                      text = "学Python\n到FishC!",
                      font = ("宋体",20),
                      fg = "white",
                      justify = LEFT,  #文字左对齐
                      image = photo,
                      compound = CENTER, #设置文本和图像的混合模式
                      )
    #文本Label对象偏移,离左窗口与上窗口都为10
    textLabel.pack(side = LEFT,padx =10,pady =10)

    mainloop()
     

    实例2:Button组件

    #导入tkinter模块的所有内容
    from tkinter import *

    def callback():
        var.set("吹吧你,我才不信呢~")

    #创建主窗口
    root = Tk()
    #设置主窗口对象的标题栏
    root.title("TK")

    frame1 = Frame(root)#框架1
    frame2 = Frame(root)#框架2

    #创建一个文本Label对象,文字为左对齐
    var = StringVar()
    var.set("您下载的影片含有未成年人限制内容,\n请满18周岁后再点击观看!")
    textLabel = Label(frame1,
                      textvariable = var, #Button显示一个StringVar的变量
                      justify = LEFT)
    #Label组件为左对齐
    textLabel.pack(side = LEFT)

    #创建一个图像Label对象
    #用PhotoImage实例化一个图片对象(支持gif格式的图片)
    photo = PhotoImage(file = "18.gif")
    imgLabel = Label(root,image = photo)
    imgLabel.pack(side = RIGHT)

    #加一个按钮
    theButton = Button(frame2,text = "已满18周岁",command = callback)
    theButton.pack()
    frame1.pack(padx = 10,pady = 10)
    frame2.pack(padx = 10,pady = 10)

    mainloop()

    066 GUI的终极选择:Tkinter3

    实例1:Checkbutton 组件

    from tkinter import *

    root = Tk()
    #需要一个Tkinter变量,用于表示该按钮是否被选中
    v = IntVar()
    c = Checkbutton(root,text="测试一下",variable = v)

    c.pack()
    #如果被选中,那么变量v被赋值为1,否则为0
    #可以用个Label标签动态地给大家展示:
    lable = Label(root,textvariable = v)
    lable.pack()

    mainloop()

    实例2:

    from tkinter import *

    root = Tk()

    GIRLS = ["貂蝉","王昭君","西施","杨玉环"]
    v = []
    for girl in GIRLS:
        v.append(girl)
        c = Checkbutton(root,text = girl,variable = v[-1])#-1表示每次取v列表中最后一个元素,即刚加入的那个元素
        c.pack(anchor = W)#W(western)向左对齐

    mainloop()

    实例3:Radiobutton 组件

    from tkinter import *

    root = Tk()

    v = IntVar()#如果被选中,v被赋值为1,否则为0
    Radiobutton(root,text = "One",variable = v,value = 1).pack(anchor = W)
    #value表示第一个按钮被选中时,v的值赋值给variable

    Radiobutton(root,text = "Two",variable = v,value = 2).pack(anchor = W)

    Radiobutton(root,text = "Three",variable = v,value = 3).pack(anchor = W)

    Radiobutton(root,text = "Four",variable = v,value = 4).pack(anchor = W)

    mainloop()

    实例4:循环处理

    from tkinter import *

    root = Tk()

    LANGS = [
        ("Python",1),
        ("Perl",2),
        ("Ruby",3),
        ("Lua",4)]
         

    v = IntVar()#如果被选中,v被赋值为1,否则为0
    v.set(1)#将1设置为默认值
    for lang,num in LANGS:
        b= Radiobutton(root,text = lang,variable = v,value = num)
        b.pack(anchor = W)
    #value表示第一个按钮被选中时,v的值赋值给variable

    mainloop()

    实例5:改成按钮形式

    from tkinter import *

    root = Tk()

    LANGS = [
        ("Python",1),
        ("Perl",2),
        ("Ruby",3),
        ("Lua",4)]
         

    v = IntVar()#如果被选中,v被赋值为1,否则为0
    v.set(1)#将1设置为默认值
    for lang,num in LANGS:
        b= Radiobutton(root,text = lang,variable = v,value = num,indicatoron = False)
        b.pack(fill = X)#表示横向填充
    #value表示第一个按钮被选中时,v的值赋值给variable

    mainloop()

    实例6:LabelFrame 组件

    from tkinter import *

    root = Tk()

    group = LabelFrame(root,text = "最好的脚本语言是?",padx = 10,pady = 10)#按钮相对边框的偏移
    group.pack(padx = 10,pady = 10)#框架相对边框的偏移

    LANGS = [
        ("Python",1),
        ("Perl",2),
        ("Ruby",3),
        ("Lua",4)]
         

    v = IntVar()#如果被选中,v被赋值为1,否则为0
    v.set(1)#将1设置为默认值
    for lang,num in LANGS:
        b= Radiobutton(group,text = lang,variable = v,value = num,indicatoron = False)
        b.pack(fill = X)
    #value表示第一个按钮被选中时,v的值赋值给variable

    mainloop()

    067 GUI的终极选择:Tkinter4

    实例1:

    from tkinter import *

    root = Tk()#创建主窗口
    e = Entry(root)#在主窗口中插入输入框
    e.pack(padx = 20,pady = 20)

    e.delete(0,END)#清空输入框
    e.insert(0,"默认文本...")#设置输入框内容

    mainloop()

    实例2:

    from tkinter import *

    def button1_show():
        print("作品:《%s》" % e1.get())#将e1.get()中得到的输入框1的内容格式化为字符串
        print("作者:%s" % e2.get())

    root = Tk()#创建主窗口

    Label(root,text = "作品:",padx = 20,pady = 10).grid(row=0,column=0)#第1行第1列,偏移是相对于当前操作组件的相邻x轴或y轴的偏移距离
    Label(root,text = "小甲鱼:").grid(row=1,column=0)#第1行第0列


    e1 = Entry(root)#在主窗口中插入输入框,文本框的内容通过e1调用
    e2 = Entry(root)#在主窗口中插入输入框
    e1.grid(row=0,column=1,padx=10)#x方向偏移是相对于"作品"的x方向偏移的;y方向偏移表示此输入框与y方向相邻物体或边框之间偏移的距离(y方向偏移)
    e2.grid(row=1,column=1,padx=10,pady=20)#x方向偏移是相对于"小甲鱼"的x方向偏移的;y方向偏移表示此输入框与y方向相邻上下物体或边框偏移的距离(y方向偏移)


    #加两个按钮
    Button1 = Button(root,text = "获取信息",command = button1_show)\
              .grid(row = 2,column = 0,sticky = W,padx = 10,pady=10)#加入反斜杠可实现分行编辑,方位设置为最西边(即靠左)
    Button2 = Button(root,text = "退出",command = root.quit).grid(row = 2,column = 1,sticky = E,padx=10)#方位设置为最东边(即靠右)

    #注:双击打开文件时退出才有效
    e1.delete(0,END)#清空输入框
    e1.insert(0,"零基础入门学习Python")#设置输入框内容

    e2.delete(1,END)#清空输入框
    e2.insert(1,"小甲鱼")#设置输入框内容

    mainloop()

    按下获取信息

    更改输入框数据,然后按下获取信息

    实例2:账号密码设置

    from tkinter import *

    def show():
        print("作品:《%s》" % e1.get())#将e1.get()中得到的输入框1的内容格式化为字符串
        print("作者:%s" % e2.get())
        e1.delete(0,END)#清空输入框1
        e2.delete(0,END)#清空输入框2

    root = Tk()#创建主窗口
    #Tkinter总共提供了三种布局组件的方法:pack()、grid()和place()
    #grid()方法允许你用表格的形式来管理组件的位置
    #row选项代表行,coulumn选项代表列
    #row = 1,column = 2表示第二行第三列(0表示第一行)

    Label(root,text = "账号:").grid(row=0)#第1行
    Label(root,text = "密码:").grid(row=1)#第2行
    v1 = StringVar()
    v2 = StringVar()

    e1 = Entry(root,textvariable = v1)#在主窗口中插入输入框,文本框的内容通过e1调用
    e2 = Entry(root,textvariable = v2,show="*")#在主窗口中插入输入框
    e1.grid(row=0,column=1,padx=10,pady=5)#x方向偏移是相对于"作品"的x方向偏移的;y方向偏移表示此输入框与y方向相邻物体或边框之间偏移的距离(y方向偏移)
    e2.grid(row=1,column=1,padx=10,pady=5)#x方向偏移是相对于"小甲鱼"的x方向偏移的;y方向偏移表示此输入框与y方向相邻上下物体或边框偏移的距离(y方向偏移)


    #可以使用sticky选项来设置组件的位置
    #使用N、E、S、W以及他们的组合NE、SE、SW、NW来表示方位

    #加两个按钮
    Button(root,text = "芝麻开门",command = show)\
              .grid(row = 2,column = 0,sticky = W,padx = 10,pady=5)#加入反斜杠可实现分行编辑,方位设置为最西边(即靠左)
    Button(root,text = "退出",command = root.quit).grid(row = 2,column = 1,sticky = E,padx=10)#方位设置为最东边(即靠右)

    mainloop()

    实例3:验证函数validatecommand

    from tkinter import *

    master = Tk()

    def test():
        if e1.get() == "小甲鱼":
            print("正确!")
            return True
        else:
            print("错误!")
            e1.delete(0, END)
            return False

    v = StringVar()

    #focusout表示Entry组件失去焦点的时候验证,调用validatecommand的test函数

    e1 = Entry(master, textvariable=v, validate="focusout", validatecommand=test)
    e2 = Entry(master)
    e1.pack(padx=10, pady=10)
    e2.pack(padx=10, pady=10)

    mainloop()
     

    实例4:invalidcommand函数

    from tkinter import *

    master = Tk()

    def test():
        if e1.get() == "小甲鱼":
            print("正确!")
            return True
        else:
            print("错误!")
            e1.delete(0, END)
            return False

    def test2():
        print("我被调用了...")

    v = StringVar()

    #focusout表示Entry组件失去焦点的时候验证,调用validatecommand的test函数
    #invalidcommand选项指定的函数只有在validatecommand的返回值为False的时候才被调用
    e1 = Entry(master, textvariable=v, validate="focusout", validatecommand=test,\
               invalidcommand=test2)
    e2 = Entry(master)
    e1.pack(padx=10, pady=10)
    e2.pack(padx=10, pady=10)

    mainloop()
     

    实例5:验证函数提供一些额外的选项

    validatecommand(f,s1,s2,...)

    其中,f是验证函数名,s1,s2,s3是额外的选项,这些选项会作为参数一次传给f函数。在此之前,需要调用register()方法将验证函数包装起来。

    from tkinter import *

    master = Tk()

    v = StringVar()

    def test(content, reason, name):
        if content == "小甲鱼":
            print("正确!")
            print(content, reason, name)
            return True
        else:
            print("错误!")
            print(content, reason, name)
            return False

    testCMD = master.register(test)
    e1 = Entry(master, textvariable=v, validate="focusout", \
               validat
               ecommand=(testCMD, '%P', '%v', '%W'))
    e2 = Entry(master)
    e1.pack(padx=10, pady=10)
    e2.pack(padx=10, pady=10)

    mainloop()
     

    实例6:设计一个 计算器

    from tkinter import *
    #计算函数
    def calc():
        result = int(v1.get())+int(v2.get())#强制转换为整型
        v3.set(result)#将result中的内容放到v3中

    #创建窗口
    root = Tk()
    #创建窗口中的一个frame框架
    frame = Frame(root)
    #设置框架位置并显示
    frame.pack(padx = 10,pady = 10)

    v1 = StringVar()
    v2 = StringVar()
    v3 = StringVar()

    #注意,这里不能使用e1.get()或者v1.get()来获取输入的内容,因为validate选项
    #指定为“key"的时候,有任何输入操作都会被拦截到这个函数中
    #也就是说先拦截,只有这个函数返回True,那么输入的内容才会到变量里去
    #所以要用%P来获取最新的输入框内容
    def test(content):
        if content.isdigit():
            return True
        else:
            return False

    #创建三个Entry组件
    testCMD = frame.register(test)
    #创建2个输入组件,输入的数据赋值给v1、v2
    e1 = Entry(frame, textvariable=v1,width=10, validate="key",\
               validatecommand=(testCMD, '%P'))
    e2 = Entry(frame, textvariable=v2,width=10, validate="key",\
               validatecommand=(testCMD, '%P'))
    #一个输出组件,设置为只读模式(readonly),v3的数据赋值给textvariable进行输出显示
    e3 = Entry(frame, textvariable=v3,width=10, validate="key",\
               validatecommand=(testCMD, '%P'),state="readonly")
    #位置设置
    e1.grid(row=0,column=0,padx=10,pady=10)
    e2.grid(row=0,column=2,padx=10)
    e3.grid(row=0,column=4,padx=10)

    #创建两个Label组件
    Label(frame,text="+").grid(row=0,column=1)
    Label(frame,text="=").grid(row=0,column=3)

    #创建一个按钮,宽度为10
    button=Button(frame,text="计算结果",width=10,command=calc)
    button.grid(row=1,column=2,pady=10)

    mainloop()

    068 GUI的终极选择:Tkinter5

    Listbox组件

    如果需要提供选项给用户选择,单选可以用Radiobutton组件,多选可以用Checkbutton,如果提供的选项非常多,可以考虑使用Listbox组件。Listbox是以列表的形式显示出来,并支持滚动条操作。

    实例1:

    from tkinter import *

    root = Tk()#创建主窗口

    theLB = Listbox(root,setgrid = True,selectmode=EXTENDED)#创建一个空列表
    theLB.pack()

    #往列表里添加数据
    for item in ["鸡蛋","鸭蛋","鹅蛋","李狗蛋"]:
        theLB.insert(END,item)#每次在列表最后插入一个数据

    #创建一个按钮,ACTIVE表示当前选中的数据
    theButton = Button(root,text="删除",command = lambda x = theLB:x.delete(ACTIVE))
    theButton.pack()

    #theLB.delete(0,END)删除所有列表数据

    mainloop()

    注:listbox.delete(0,END)可以删除列表中所有项目

    实例2:添加height选项

    from tkinter import *

    root = Tk()#创建主窗口

    #height=11表示可以显示11个项目
    theLB = Listbox(root,setgrid = True,\
                    selectmode=BROWSE,height=11)#创建一个空列表,选择模式为单选
    theLB.pack()

    #往列表里添加数据
    for item in range(11):
        theLB.insert(END,item)#每次在列表最后插入一个数据

    #创建一个按钮,ACTIVE表示当前选中的数据
    theButton = Button(root,text="删除",command = lambda x = theLB:x.delete(ACTIVE))
    theButton.pack()

    #theLB.delete(0,END)删除所有列表数据

    mainloop()

    Scrollbar组件

    实例1:

    from tkinter import *

    root = Tk()#创建主窗口

    sb = Scrollbar(root)
    sb.pack(side=RIGHT,fill=Y)

    lb = Listbox(root,yscrollcommand=sb.set)#创建一个空列表
    for i in range(1000):
        lb.insert(END,i)
    lb.pack(side=LEFT,fill=BOTH)

    sb.config(command = lb.yview)

    mainloop()

    事实上,这是一个互联互通的过程。当用户操作滚动条时,滚动条响应滚动并同时通过Listbox组件的yview()方法滚动列表框里的内容;同样,当列表框中可视范围发生改变的时候,Listbox组件通过调用Scrollbar组件的set()方法设置滚动条的最新位置。

    Scale组件

    Scale组件主要是通过滑块来表示某个范围内的一个数字,可以通过修改选项设置范围以及分辨率(精度)

    实例1:

    from tkinter import *

    root = Tk()#创建主窗口
    Scale(root,from_=0,to=42).pack()#创建铅锤方向滚动条
    Scale(root,from_=0,to=200,orient=HORIZONTAL).pack()#创建水平方向滚动条

    mainloop()

    实例2:打印当前位置

    from tkinter import *

    def show():
        print(s1.get(),s2.get())#使用get()方法获取当前滑块的位置

    root = Tk()#创建主窗口
    s1 = Scale(root,from_=0,to=42)#创建铅锤方向滚动条
    s1.pack()
    s2 = Scale(root,from_=0,to=200,orient=HORIZONTAL)#创建水平方向滚动条
    s2.pack()

    #创建一个按钮
    Button(root,text="获取位置",command=show).pack()

    mainloop()

    实例3:通过resolution选项控制分辨率(步长),通过tickinterval选项设置刻度

    from tkinter import *

    def show():
        print(s1.get(),s2.get())#使用get()方法获取当前滑块的位置

    root = Tk()#创建主窗口
    #tickinterval表示设置刻度,即每隔多少显示一个刻度
    #length表示滚动条的长度所占的像素数
    #resolution用来控制分辨率(步长)
    s1 = Scale(root,from_=0,to=42,tickinterval=5,length=200,\
               resolution=5,orient=VERTICAL)#创建铅锤方向滚动条
    s1.pack()
    s2 = Scale(root,from_=0,to=200,tickinterval=10,\
               length=600,orient=HORIZONTAL)#创建水平方向滚动条
    s2.pack()

    #创建一个按钮
    Button(root,text="获取位置",command=show).pack()

    mainloop()

    069 GUI的终极选择:Tkinter6

    Text组件

    Text(文本)组件用于显示和处理多种任务。虽然该组件的主要目的是显示多行文本,但它常常也被用于作为简单的文本编辑器和网页浏览器使用。

    实例1:插入内容

    from tkinter import *

    root = Tk()
    text = Text(root,width=30,height=2)
    text.pack()
    #INSERT索引表示插入光标当前的位置
    text.insert(INSERT,"I love\n")#光标当前的位置插入

    #END,对应Text组件的文本缓存区最后一个字符的下一个位置
    text.insert(END,"FishC.com!")

    mainloop()

    实例2:插入image对象windows组件

    from tkinter import *

    def show():
        print("哟,我被点了一下~")

    root = Tk()
    text = Text(root,width=30,height=5)
    text.pack()

    #INSERT索引表示插入光标当前的位置
    text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入

    #创建一个按钮
    b1=Button(root,text="点我点我",command=show)
    text.window_create(INSERT,window=b1)

    mainloop()
     

    实例3:单击按钮显示一张图片

    from tkinter import *

    def show():
        text.image_create(INSERT,image=photo)

    root = Tk()
    text = Text(root,width=30,height=50)
    text.pack()

    #INSERT索引表示插入光标当前的位置
    text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入

    photo = PhotoImage(file='fishc.gif')

    #创建一个按钮
    b1=Button(root,text="点我点我",command=show)
    text.window_create(INSERT,window=b1)

    mainloop()

    Indexer用法

    实例1:“line.column”

    from tkinter import *

    root = Tk()
    text = Text(root,width=30,height=5)
    text.pack()

    #INSERT索引表示插入光标当前的位置
    text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入
    #注意,行号从1开始,列号则从0开始
    print(text.get(1.2,1.6))#获取第一行第2列到第一行第六列的数据

    mainloop()

    实例2:“line.end”

    行号加上字符串".end"格式表示为该行最后一个字符的位置

    实例:

    from tkinter import *

    root = Tk()
    text = Text(root,width=30,height=5)
    text.pack()

    #INSERT索引表示插入光标当前的位置
    text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入
    #注意,行号从1开始,列号则从0开始
    print(text.get("1.2","1.end"))#获取第一行第2列到第一行第六列的数据

    mainloop()

    Mask用法

    mask(标记)通常是嵌入到Text组件文本中的不可见对象。事实上,Marks是指定字符间的位置,并跟随相应的字符一起移动。

    实例:Mark事实上就是索引,用于表示位置

    from tkinter import *

    root = Tk()
    text = Text(root,width=30,height=5)
    text.pack()

    #INSERT索引表示插入光标当前的位置
    text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入
    #注意,行号从1开始,列号则从0开始
    text.mark_set("here","1.2")#设置光标位置为1.2
    text.insert("here","插")

    mainloop()

    实例2:如果Mark前面的内容发生改变,Mark的位置也会跟着移动

    from tkinter import *

    root = Tk()
    text = Text(root,width=30,height=5)
    text.pack()

    #INSERT索引表示插入光标当前的位置
    text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入
    #注意,行号从1开始,列号则从0开始
    text.mark_set("here","1.2")#设置当前光标位置为1.2
    text.insert("here","插")#执行后当前光标位置(Mark位置)变成了1.3
    text.insert("here","入")
    #text.insert("1.3","入")

    mainloop()

    实例3:如果Mark周围的文本被删除了,Mark仍然存在

    from tkinter import *

    root = Tk()
    text = Text(root,width=30,height=5)
    text.pack()

    #INSERT索引表示插入光标当前的位置
    text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入
    #注意,行号从1开始,列号则从0开始
    text.mark_set("here","1.2")#设置当前光标位置为1.2
    text.insert("here","插")#执行后当前光标位置变成了1.3
    text.delete("1.0",END)
    text.insert("here","入")#here表示当前Mark的位置,如果Mark左边并没有数据则会插入到最左边

    mainloop()

    例4:只有mark_unset()方法可以解除Mark的封印

    from tkinter import *

    root = Tk()
    text = Text(root,width=30,height=5)
    text.pack()

    #INSERT索引表示插入光标当前的位置
    text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入
    #注意,行号从1开始,列号则从0开始
    text.mark_set("here","1.2")#设置当前光标位置为1.2
    text.insert("here","插")#执行后当前光标位置变成了1.3
    text.mark_unset("here")

    text.delete("1.0",END)
    text.insert("here","入")#here表示当前Mark的位置

    mainloop()

    默认插入内容是插入到Mark左侧(就是说插入一个字符后,Mark向后移动了一个字符的位置)

    实例5:插入内容到Mark的右侧

    from tkinter import *

    root = Tk()
    text = Text(root,width=30,height=5)
    text.pack()

    #INSERT索引表示插入光标当前的位置
    text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入
    #注意,行号从1开始,列号则从0开始
    text.mark_set("here","1.2")#设置当前Mark位置为1.2
    text.mark_gravity("here",LEFT)

    text.insert("here","插")#执行后当前Mark位置变成了1.3
    text.insert("here","入")#here表示当前Mark的位置

    mainloop()

    070 GUI的终极选择:Tkinter7

    实例1:添加Tags

    from tkinter import *

    root = Tk()
    text = Text(root,width=30,height=5)
    text.pack()

    #INSERT索引表示插入光标当前的位置
    text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入
    #注意,行号从1开始,列号则从0开始
    text.tag_add("tag1","1.7","1.12","1.14")#1.7(第一行第八列)到1.12,,与1.14设置Tag样式
    text.tag_config("tag1",background ="yellow",foreground="red")

    mainloop()

    实例2:Tags覆盖

    from tkinter import *

    root = Tk()
    text = Text(root,width=30,height=5)
    text.pack()

    #INSERT索引表示插入光标当前的位置
    text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入
    #注意,行号从1开始,列号则从0开始
    text.tag_add("tag1","1.7","1.12","1.14")#1.7(第一行第八列)到1.12,,与1.14设置Tag样式
    text.tag_add("tag2","1.7","1.12","1.14")#1.7(第一行第八列)到1.12,,与1.14设置Tag样式

    text.tag_config("tag1",background ="yellow",foreground="red")
    text.tag_config("tag2",background ="blue")

    mainloop()
     

    实例2:降低Tag优先级

    from tkinter import *

    root = Tk()
    text = Text(root,width=30,height=5)
    text.pack()

    #INSERT索引表示插入光标当前的位置
    text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入
    #注意,行号从1开始,列号则从0开始
    text.tag_add("tag1","1.7","1.12","1.14")#1.7(第一行第八列)到1.12,,与1.14设置Tag样式
    text.tag_add("tag2","1.7","1.12","1.14")#1.7(第一行第八列)到1.12,,与1.14设置Tag样式

    text.tag_config("tag1",background ="yellow",foreground="red")
    text.tag_config("tag2",background ="blue")

    text.tag_lower("tag2")#降低tag2的优先级

    mainloop()

    实例3:Tags事件绑定

    from tkinter import *
    import webbrowser#导入网页模块

    def show_hand_cursor(event):
        text.config(cursor="arrow")

    def show_arrow_cursor(event):
        text.config(cursor="xterm")

    def click(event):
        webbrowser.open("http://www.fishc.com")
        
    root = Tk()
    text = Text(root,width=30,height=5)
    text.pack()

    #INSERT索引表示插入光标当前的位置
    text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入
    #注意,行号从1开始,列号则从0开始
    text.tag_add("link","1.7","1.16")#1.7(第一行第八列)到1.16
    #设置蓝色前景色并底部划线
    text.tag_config("link",foreground="blue",underline=True)

    #当进入绑定文本段时,鼠标样式切换为“arrow"形态
    text.tag_bind("link","<Enter>",show_hand_cursor)
    #当离开绑定文本段时,鼠标样式切换为“xterm"形态
    text.tag_bind("link","<Leave>",show_arrow_cursor)
    #当触发鼠标“左键单击”时,使用默认浏览器打开鱼C网址
    text.tag_bind("link","<Button-1>",click)

    mainloop()

    实例4:判断内容是否发生改变

    from tkinter import *
    import hashlib

    def getSig(contents):
        m = hashlib.md5(contents.encode())
        return m.digest()

    def check():#检查
        contents = text.get(1.0,END)
        if sig!=getSig(contents):
            print("警报,内容发生变动")
        else:
            print("风平浪静")
        
    root = Tk()
    text = Text(root,width=30,height=5)
    text.pack()

    #INSERT索引表示插入光标当前的位置
    text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入
    #注意,行号从1开始,列号则从0开始
    #获取文本内容
    contents=text.get(1.0,END)

    sig = getSig(contents)

    Button(root,text="检查",command=check).pack()

    mainloop()

    实例5:查找操作(使用search()方法可以搜索Text组件中的内容)

    from tkinter import *
    import hashlib

    #将任何格式的索引号统一为元组(行,列)的格式输出
    def getIndex(text,index):
        #split这里以"."拆分字符串,将1.3拆分为字符1和3,然后通过map将字符转换为整型
        return tuple(map(int,str.split(text.index(index),".")))
        
    root = Tk()
    text = Text(root,width=30,height=5)
    text.pack()

    #INSERT索引表示插入光标当前的位置
    text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入

    #将任何格式的索引号统一为元组(行、列)的格式输出
    start = 1.0
    while True:
        pos = text.search("o",start,stopindex=END)#从开始到结束全文搜索
        if not pos:
            break
        print("找到了,位置是:",getIndex(text,pos))
        start = pos + "+1c"#将start指向找到的字符位置的下一个字符,以便进行下一次搜索

    mainloop()

    Text组件内部有一个栈专门用于记录内容的每次变动,所以每次“撤销”操作就是一次弹栈操作,“恢复”就是再次压栈。

    实例6:撤销

    from tkinter import *

    #将任何格式的索引号统一为元组(行,列)的格式输出
    def show():
        text.edit_undo()
        
    root = Tk()
    text = Text(root,width=30,height=5,undo=True)
    text.pack()
    text.insert(INSERT,"I love FishC")

    Button(root,text="撤销",command=show).pack()

    mainloop()


    实例7:每次撤销一个字符

    from tkinter import *

    def callback(event):
        text.edit_separator()

    def show():
        text.edit_undo()#执行撤回操作
        
    root = Tk()

    #autoseparators表示一次完整的操作结束后自动插入“分隔符”,此处设置为False
    text = Text(root,width=30,height=5,autoseparators=False,undo=True,maxundo=10)
    text.pack()

    text.insert(INSERT,"I love FishC!")
    text.bind('<Key>',callback)#每次有输入就插入一个“分隔符”

    Button(root,text="撤销",command=show).pack()

    mainloop()

    071 GUI的终极选择:Tkinter8

    Canvas(画布)组件

    一个可以让你随心所欲绘制界面的组件。通常用于显示和编辑图形,可以用它来绘制直线、图形、多边形,甚至是绘制其他组件。

    实例1:

    from tkinter import *
    root = Tk()
    #创建canvas对象框,设置其宽度、高度与背景色
    w = Canvas(root,width=200,height=100,background="black")
    w.pack()

    #画一条黄色的线
    w.create_line(0,50,200,50,fill="yellow")
    #画一条红色的竖线(虚线)
    w.create_line(100,0,100,100,fill="red")
    #中间画一个蓝色的矩形
    w.create_rectangle(50,25,150,75,fill="blue")

    mainloop()

    实例2:

    from tkinter import *
    root = Tk()
    #创建canvas对象框,设置其宽度、高度与背景色
    w = Canvas(root,width=200,height=100,background="black")
    w.pack()

    #画一条黄色的线(参数为其x、y轴坐标)
    line1 = w.create_line(0,50,200,50,fill="yellow")
    #画一条红色的竖线(虚线)
    line2 = w.create_line(100,0,100,100,fill="red")
    #中间画一个蓝色的矩形
    rect1 = w.create_rectangle(50,25,150,75,fill="blue")

    w.coords(line1,0,25,200,25)#将line1移动到新的坐标
    w.itemconfig(rect1,fill="red")#重新设置矩形的填充色为红色
    w.delete(line2)#删除线2

    #创建一个按钮,按下时删除所有图形
    Button(root,text="删除全部",command=(lambda x=ALL:w.delete(x))).pack()

    mainloop()

    实例3:在Canvas上显示文本

    from tkinter import *
    root = Tk()
    #创建canvas对象框,设置其宽度、高度与背景色
    w = Canvas(root,width=200,height=100,background="black")
    w.pack()

    #画一条绿色的斜线(参数为其x、y轴坐标),宽度为三个像素点
    line1 = w.create_line(0,0,200,100,fill="green",width=3)
    #画一条绿色的斜线
    line2 = w.create_line(200,0,0,100,fill="green",width=3)
    #中间画两个矩形
    rect1 = w.create_rectangle(40,20,160,80,fill="blue")
    rect2 = w.create_rectangle(60,30,140,70,fill="yellow")
    #在矩形正中(默认)显示文本,坐标为文本正中坐标
    w.create_text(100,50,text="Hadley")

    #创建一个按钮,按下时删除所有图形
    Button(root,text="删除全部",command=(lambda x=ALL:w.delete(x))).pack()

    mainloop()

    实例4:绘制椭圆

    from tkinter import *
    root = Tk()
    #创建canvas对象框,设置其宽度、高度与背景色
    w = Canvas(root,width=200,height=100,background="white")
    w.pack()

    #绘制一个虚线的矩形
    w.create_rectangle(40,20,160,80,dash=(4,4))
    #绘制椭圆,粉色填充
    w.create_oval(40,20,160,80,fill="pink")
    #在矩形正中(默认)显示文本,坐标为文本正中坐标
    w.create_text(100,50,text="Hadley")

    mainloop()
    实例5:绘制圆形

    from tkinter import *
    root = Tk()
    #创建canvas对象框,设置其宽度、高度与背景色
    w = Canvas(root,width=200,height=100,background="white")
    w.pack()

    #绘制一个虚线的矩形
    w.create_rectangle(40,20,160,80,dash=(4,4))
    #绘制圆形,粉色填充
    #w.create_oval(40,20,160,80,fill="pink")
    w.create_oval(70,20,130,80,fill="pink")
    #在矩形正中(默认)显示文本,坐标为文本正中坐标
    w.create_text(100,50,text="Hadley")

    mainloop()

    实例6:绘制多边形

    from tkinter import *
    import math as m

    root = Tk()
    w=Canvas(root,width=200,height=150,background="red")
    w.pack()
    center_x = 100
    center_y = 80
    r = 70
    points = [
        #左上角A
        center_x - int(r*m.sin(2*m.pi/5)),
        center_y - int(r*m.cos(2*m.pi/5)),
        #右上角C
        center_x + int(r*m.sin(2*m.pi/5)),
        center_y - int(r*m.cos(2*m.pi/5)),
        #左下角E
        center_x - int(r*m.sin(m.pi/5)),
        center_y + int(r*m.cos(m.pi/5)),
        #顶点D
        center_x,
        center_y - r,
        #右下角B
        center_x + int(r*m.sin(m.pi/5)),
        center_y + int(r*m.cos(m.pi/5)),
        ]
    #创建多边形方法,会自动按ACEDBA的形式连线,如果构成闭环,则会自动填充
    w.create_polygon(points,outline="green",fill="yellow")

    w.create_text(100,80,text="Hadley")

    mainloop()

    实例7:

    from tkinter import *

    root = Tk()
    w=Canvas(root,width=400,height=200,background="white")
    w.pack()

    def paint(event):#画小圆
        x1,y1 = (event.x - 1),(event.y -1)
        x2,y2 = (event.x + 1),(event.y +1)
        w.create_oval(x1,y1,x2,y2,fill="red")

    w.bind("<B1 - Motion>",paint)#画布与鼠标进行绑定
    Label(root,text="按住鼠标左键并移动,开始绘制你的理想蓝图吧。。。").pack(side=BOTTOM)

    mainloop()

    073 GUI的终极选择:Tkinter10

    Munu组件

    Tkinter提供了一个Menu组件,用于实现顶级菜单、下拉菜单和弹出菜单。

    实例1:创建一个顶级菜单(或称窗口主菜单

    from tkinter import *

    def callback():
        print("被调用了")
        
    root = Tk()


    menubar = Menu(root)#创建一个顶级菜单
    menubar.add_command(label="Hello",command=callback)#创建一个顶级菜单对象
    menubar.add_command(label="Quit",command=root.quit)

    #显示菜单
    root.config(menu=menubar)

    mainloop()

    实例2:创建添加到主菜单上的下拉菜单

    from tkinter import *

    def callback():
        print("被调用了")
        
    root = Tk()

    #创建一个顶级菜单
    menubar = Menu(root)

    #创建下拉菜单filemenu包含内容
    filemenu=Menu(menubar,tearoff=False)#创建一个从属于menubar的子菜单(下拉菜单)filemenu
    filemenu.add_command(label="打开",command=callback)#创建一个下拉菜单对象
    filemenu.add_command(label="保存",command=callback)
    filemenu.add_separator()#插入分隔线
    filemenu.add_command(label="退出",command=root.quit)
    #创建一个顶级菜单对象“文件”,filemenu从属于这个对象(或称将filemenu添加到顶级菜单“文件”中)
    menubar.add_cascade(label="文件",menu=filemenu)

    #创建另一个下拉菜单editmenu包含内容
    editmenu=Menu(menubar,tearoff=False)#创建一个从属于menubar的子菜单(下拉菜单)editmenu
    editmenu.add_command(label="剪切",command=callback)
    editmenu.add_command(label="拷贝",command=callback)
    editmenu.add_separator()#插入分隔线
    editmenu.add_command(label="粘贴",command=callback)
    #创建一个顶级菜单对象“编辑”,editmenu从属于这个对象(或称将editmenu添加到顶级菜单“编辑”中)
    menubar.add_cascade(label="编辑",menu=editmenu)

    #显示菜单
    root.config(menu=menubar)

    mainloop()

    实例3:创建一个弹出菜单方法

    from tkinter import *

    def callback():
        print("被调用了")
        
    root = Tk()

    def popup(event):
        menu.post(event.x_root,event.y_root)#在此时鼠标位置弹出显示窗口
        
    #创建一个顶级菜单menu
    menu = Menu(root,tearoff=False)

    #创建顶级菜单menu包含内容
    menu.add_command(label="撤销",command=callback)#创建一个顶级菜单对象
    menu.add_command(label="重做",command=callback)
    #创建一个框架
    frame = Frame(root,width=100,height=100)
    frame.pack()

    #将鼠标右键与popup方法绑定
    frame.bind("<Button-3>",popup)

    #显示菜单
    #root.config(menu=menu)

    mainloop()

    实例4:菜单弹出

    from tkinter import *

    def callback():
        print("被调用了")
        
    root = Tk()

    def popup(event):
        menu.post(event.x_root,event.y_root)#在此时鼠标位置弹出显示窗口
        
    #创建一个顶级菜单menu
    menu = Menu(root,tearoff=True)

    #创建顶级菜单menu包含内容
    menu.add_command(label="撤销",command=callback)#创建一个顶级菜单对象
    menu.add_command(label="重做",command=callback)
    #创建一个框架
    frame = Frame(root,width=500,height=500)
    frame.pack()

    #将鼠标右键与popup方法绑定
    frame.bind("<Button-3>",popup)

    #显示菜单
    #root.config(menu=menu)

    mainloop()

    实例5:添加单选组件radiobutton和多选按钮checkbutton

    from tkinter import *

    def callback():
        print("被调用了")
        
    root = Tk()

    #创建一个顶级菜单
    menubar = Menu(root)
    #创建checkbutton关联变量
    openVar = IntVar()
    saveVar = IntVar()
    exitVar = IntVar()
    #创建下拉菜单filemenu包含内容
    filemenu=Menu(menubar,tearoff=True)#创建一个从属于menubar的子菜单(下拉菜单)filemenu
    filemenu.add_checkbutton(label="打开",command=callback,variable=openVar)#创建一个下拉菜单对象
    filemenu.add_checkbutton(label="保存",command=callback,variable=saveVar)
    filemenu.add_separator()#插入分隔线
    filemenu.add_checkbutton(label="退出",command=root.quit,variable=exitVar)
    #创建一个顶级菜单对象“文件”,filemenu从属于这个对象(或称将filemenu添加到顶级菜单“文件”中)
    menubar.add_cascade(label="文件",menu=filemenu)

    #创建radiobutton关联变量
    editVar = IntVar()
    editVar.set(1)

    #创建另一个下拉菜单editmenu包含内容
    editmenu=Menu(menubar,tearoff=True)#创建一个从属于menubar的子菜单(下拉菜单)editmenu
    editmenu.add_radiobutton(label="剪切",command=callback,variable=editVar,value=1)
    editmenu.add_radiobutton(label="拷贝",command=callback,variable=editVar,value=2)
    editmenu.add_separator()#插入分隔线
    editmenu.add_radiobutton(label="粘贴",command=callback,variable=editVar,value=3)
    #创建一个顶级菜单对象“编辑”,editmenu从属于这个对象(或称将editmenu添加到顶级菜单“编辑”中)
    menubar.add_cascade(label="编辑",menu=editmenu)

    #显示菜单
    root.config(menu=menubar)

    mainloop()

    Menubutton组件(希望菜单按钮出现在其它位置时)

    Menubutton组件是一个与Menu组件相关联的按钮,它可以放在窗口中的任意位置,并且在被按下时弹出下拉菜单

    实例1:

    from tkinter import *

    def callback():
        print("被调用了")
        
    root = Tk()

    #创建一个顶级菜单Menubutton按钮,设置为浮起显示(RAISED)
    mb = Menubutton(root,text="点我",relief=RAISED)

    mb.pack(side=RIGHT)#设置为右中显示

    #创建下拉菜单filemenu包含内容
    filemenu = Menu(mb,tearoff=False)#创建一个从属于mb的下拉菜单filemenu
    filemenu.add_checkbutton(label="打开",command=callback,selectcolor="yellow")
    filemenu.add_command(label="保存",command=callback)#创建一个下拉菜单对象"保存“
    filemenu.add_separator()
    filemenu.add_command(label="退出",command=root.quit)
    #显示菜单
    mb.config(menu=filemenu)

    mainloop()

    OptionMenu(选项菜单)组件

    选项菜单的发明弥补了Listbox组件无法实现下拉列表框的遗憾

    实例1:

    from tkinter import *

    def callback():
        print("被调用了")
        
    root = Tk()

    variable = StringVar()#创建字符串变量variable
    variable.set("one")#初始值设置为"one"
    w = OptionMenu(root,variable,"one","two","three")
    w.pack()

    mainloop()

    实例2:多个选项添加到选项菜单中

    from tkinter import *

    def callback():
        print("被调用了")
        
    root = Tk()

    OPTIONS = [
        "Hadley",
        "小土豆",
        "yiwofeiye",
        "RAN"
        ]

    variable = StringVar()#创建字符串变量variable
    variable.set(OPTIONS[0])#初始值设置为"one"
    w = OptionMenu(root,variable,*OPTIONS)
    w.pack()

    def callback():
        print(variable.get())

    Button(root,text="点我",command=callback).pack()

    mainloop()
     

    074  GUI的终极选择:Tkinter11

    事件绑定

    对于每个组件来说,可以通过bind()方法将函数或方法绑定到具体的事件上。当被触发的事件满足该组件绑定的事件时,Tkinter就会带着事件描述去调用handler()方法

    实例1:捕获单击鼠标位置

    from tkinter import*

    root = Tk()

    def callback(event):
        print("点击位置:",event.x,event.y)

    frame = Frame(root,width=200,height=200)
    #Button表示鼠标点击事件
    #1代表左键 2代表中间滚轮点击 3代表右键
    frame.bind("<Button-1>",callback)#按键按下时,调用callback方法
    frame.pack()

    mainloop()

    实例2:捕获键盘事件

    #捕获单击鼠标的位置
    from tkinter import*

    root = Tk()

    def callback(event):
        print("敲击位置:",repr(event.char))#打印当前按下按键的字符
        print(event.char)

    frame = Frame(root,width=200,height=200)
    #Key为键盘事件
    frame.bind("<Key>",callback)#按键按下时,调用callback方法
    frame.focus_set()#获得焦点
    frame.pack()

    mainloop()

    实例3:捕获鼠标在组件上的运动轨迹

    #当鼠标在组件内移动的整个过程均触发该事件

    from tkinter import*

    root = Tk()

    def callback(event):
        print("当前位置:",event.x,event.y)#打印当前按下按键的字符

    frame = Frame(root,width=200,height=200)
    frame.bind("<Motion>",callback)#按键按下时,调用callback方法
    frame.pack()

    mainloop()

    事件序列

    Tkinter使用一种称为事件序列的机制来允许用户定义事件,用户需要使用bind()方法将具体的事件序列与自定义的方法绑定

    Event对象(按键名keysym和按键码keycode)

    实例1:打印当前按下按键的按键名

    from tkinter import*

    root = Tk()

    def callback(event):
        print(event.keysym)#打印当前按下按键的按键名
        print(event.char)

    frame = Frame(root,width=200,height=200)
    #Key为键盘事件
    frame.bind("<Key>",callback)#按键按下时,调用callback方法
    frame.focus_set()#获得焦点
    frame.pack()

    mainloop()

    075 GUI的终极选择:Tkinter12

    Message组件

    Message(消息)组件是Label组件的变体,用于显示多行文本信息。Message组件能够自动换行,并调整文本的尺寸使其适应给定得尺寸。

    实例1:

    from tkinter import *

    root = Tk()
    w1 = Message(root,text="这是一则消息",width=100)
    w1.pack()
    w2 = Message(root,text="这是一条骇人听闻的长消息!",width=100)
    w2.pack()

    mainloop()

    Spinbox组件

    Entry组件的变体,用于从一些固定的值中选取一个。使用Spinbox组件,可以通过返回或者元组指定允许用户输入的内容。

    实例1:

    from tkinter import *

    root = Tk()

    #w = Spinbox(root,from_=0,to=10)#指定输入值为0-10
    w = Spinbox(root,value=("Hadley","小土豆","雅馨"))#指定输入
    w.pack()

    mainloop()

    PanedWindow组件

    与Frame类似,都是为组件提供一个框架,但其还允许让用户调整应用程序的空间划分

    实例1:两窗格

    from tkinter import *

    root = Tk()

    m = PanedWindow(orient = VERTICAL)#设置为上下分布
    m.pack(fill=BOTH,expand=1)#设置为框架覆盖全局

    top = Label(m,text="top pane")#顶窗格
    m.add(top)

    bottom = Label(m,text="bottom pane")#底窗格
    m.add(bottom)

    mainloop()

    实例2:三窗格

    from tkinter import *

    root = Tk()

    m1 = PanedWindow()#默认为左右分布
    m1.pack(fill=BOTH,expand=1)
    left = Label(m1,text="left pane")#左窗格
    m1.add(left)

    m2 = PanedWindow(orient=VERTICAL)
    m1.add(m2)
    top=Label(m2,text="top pane")#顶窗格
    m2.add(top)
    bottom = Label(m2,text="bottom pane")#底窗格
    m2.add(bottom)

    mainloop()

    实例3:显示“分割线”

    from tkinter import *

    root = Tk()

    #showhandle=True表示显示“手柄”
    #sashrelief=SUNKEN表示分隔线的样式设置为向下凹
    m1 = PanedWindow(showhandle=True,sashrelief=SUNKEN)
    m1.pack(fill=BOTH,expand=1)
    left = Label(m1,text="left pane")
    m1.add(left)

    m2 = PanedWindow(orient=VERTICAL,showhandle=True,sashrelief=SUNKEN)
    m1.add(m2)
    top=Label(m2,text="top pane")
    m2.add(top)
    bottom = Label(m2,text="bottom pane")
    m2.add(bottom)

    mainloop()

    Toplevel组件

    Topleve(顶级窗口)l组件类似于Frame组件,但其是一个独立的顶级窗口,通常拥有标题栏、边框等部件。通常用在显示额外的窗口、对话框和其他弹出窗口中。

    实例1:按钮按下创建一个顶级窗口

    from tkinter import *

    def create():
        top = Toplevel()#创建一个独立的顶级窗口
        top.title("FishC Demo")
        msg = Message(top,text="I love FishC.com")
        msg.pack()
        
    root = Tk()
    Button(root,text="创建顶级窗口",command=create).pack()

    mainloop()

    实例2:Toplevel的窗口设置为50%透明

    from tkinter import *

    def create():
        top = Toplevel()
        top.title("FishC Demo")
        top.attributes("-alpha",0.5)#设置为50%透明度
        msg = Message(top,text="I love FishC.com")
        msg.pack()
        
    root = Tk()
    Button(root,text="创建顶级窗口",command=create).pack()

    mainloop()

    076 GUI的终极选择:Tkinter13

    布局管理器

    布局管理器就是管理你的那些组件如何排列的家伙。Tkinter有三个布局管理器,分别是pack、grid和place

    pack:按添加顺序排列组件

    grid:按行/列形式排列组件

    place:允许程序员指定组件的大小和位置

    pack

    实例1:生成一个Listbox组件并将它填充到root窗口

    from tkinter import *

    root = Tk()
    listbox = Listbox(root)
    #fill选项是告诉窗口管理器该组件将怎样填充整个分配给它的空间
    #BOTH表示同时横向和纵向扩展;X表示横向;Y表示纵向
    #expand选项是告诉窗口管理器是否将父组件的额外空间也填满(任意拉伸窗口依旧会填满)

    #默认情况下pack是将添加的组件依次纵向排列
    listbox.pack(fill=BOTH,expand=True)
    for i in range(10):
        listbox.insert(END,str(i))

    mainloop()

    实例2:纵向排列,横向填充

    from tkinter import *

    root = Tk()
    #fill选项是告诉窗口管理器该组件将怎样填充整个分配给它的空间
    #BOTH表示同时横向和纵向扩展;X表示横向;Y表示纵向
    #expand选项是告诉窗口管理器是否将父组件的额外空间也填满

    #默认情况下pack的side属性是将添加的组件依次纵向排列
    Label(root, text="red", bg="red", fg="white").pack(fill=X)
    Label(root, text="green", bg="green", fg="black").pack(fill=X)
    Label(root, text="blue", bg="blue", fg="white").pack(fill=X)

    mainloop()

    实例3:横向排列,纵向填充

    from tkinter import *

    root = Tk()
    #fill选项是告诉窗口管理器该组件将怎样填充整个分配给它的空间
    #BOTH表示同时横向和纵向扩展;X表示横向;Y表示纵向
    #expand选项是告诉窗口管理器是否将父组件的额外空间也填满

    #将pack设置为横向排列
    Label(root, text="red", bg="red", fg="white").pack(side=LEFT)
    Label(root, text="green", bg="green", fg="black").pack(side=LEFT)
    Label(root, text="blue", bg="blue", fg="white").pack(side=LEFT)

    mainloop()

    grid

    使用一个grid就可以简单地实现你用很多个框架和pack搭建起来的效果。使用grid排列组件,只需告诉它你想要将组件放置的位置(行row/列column)。

    实例1:

    from tkinter import *

    root = Tk()

    #column默认值是0
    #默认情况下组件会居中显示在对应的网格里
    #Label(root,text="用户名").grid(row=0)
    #Label(root,text="密码").grid(row=1)
    #设置sticky=W使Label左对齐
    Label(root,text="用户名").grid(row=0,sticky=W)#左对齐
    Label(root,text="密码").grid(row=1,sticky=W)

    Entry(root).grid(row=0,column=1)
    Entry(root,show="*").grid(row=1,column=1)

    mainloop()

    实例2:设置rowspan与columnspan实现跨行和跨列功能

    from tkinter import *

    root = Tk()

    #column默认值是0
    #默认情况下组件会居中显示在对应的网格里
    #Label(root,text="用户名").grid(row=0)
    #Label(root,text="密码").grid(row=1)
    #设置sticky=W使Label左对齐
    #创建Label文本
    Label(root,text="用户名").grid(row=0,sticky=W)
    Label(root,text="密码").grid(row=1,sticky=W)
    #创建输入
    Entry(root).grid(row=0,column=1)
    Entry(root,show="*").grid(row=1,column=1)
    #插入Label图像
    photo = PhotoImage(file="logo.gif")
    #rowspan=2跨两行,边距5
    Label(root,image=photo).grid(row=0,column=2,rowspan=2,padx=5,pady=5)
    #columnspan=3跨三列(默认为居中显示),边距5
    Button(text="提交",width=10).grid(row=2,columnspan=3,pady=5)

    mainloop()

    place

    通常情况下不建议使用place布局管理器

    实例1:将子组件显示在父组件的正中间

    from tkinter import *

    def callback():
        print("正中靶心")
    root = Tk()
    #relx和rely指定的是子组件相对于父组件的位置,范围是(00`1.0),0.5则表示一半,正中间
    #anchor=CENTER表示正中显示
    Button(root,text="点我",command=callback).place(relx=0.5,rely=0.5,anchor=CENTER)

    mainloop()

    实例2:Button组件覆盖Label组件

    from tkinter import *

    def callback():
        print("正中靶心")
    root = Tk()

    photo = PhotoImage(file="logo_big.gif")
    Label(root,image=photo).pack()
    #relx和rely指定的是子组件相对于父组件的位置,范围是(00`1.0),0.5则表示一半,正中间
    Button(root,text="点我",command=callback).place(relx=0.5,rely=0.5,anchor=CENTER)

    mainloop()

    实例3:

    from tkinter import *

    root = Tk()

    #relx和rely指定的是子组件相对于父组件的位置,范围是(00`1.0),0.5则表示一半,正中间
    #relwidth和relheight选项指定相对父组件的尺寸
    Label(root,bg="red").place(relx=0.5,rely=0.5,relheight=0.75,relwidth=0.75,anchor=CENTER)
    Label(root,bg="yellow").place(relx=0.5,rely=0.5,relheight=0.5,relwidth=0.5,anchor=CENTER)
    Label(root,bg="green").place(relx=0.5,rely=0.5,relheight=0.25,relwidth=0.25,anchor=CENTER)

    mainloop()

    077 GUI的终极选择:Tkinter14

    Tkinter提供了三种标准对话框模块,分别是:messagebox、filedialog、colorchooser

    messagebox(消息对话框)

    实例1:askokcancel函数

    from tkinter import *

    print(messagebox.askokcancel("FishC Demo","发射核弹?"))

    mainloop()

    实例2:askquestion函数

    实例3:asiretrycancel函数

    实例4:askyesno函数

    实例5:showerror函数

    from tkinter import *

    #print(messagebox.askokcancel("FishC Demo","发射核弹?"))
    #print(messagebox.askquestion("FishC Demo","买个U盘?"))
    #print(messagebox.askretrycancel("FishC Demo","启动失败,重启?"))
    #print(messagebox.askyesno("FishC Demo","你确定要格式化硬盘吗?"))
    print(messagebox.showerror("FishC Demo","Error!!!"))

    mainloop()

    实例6:showinfo函数

    from tkinter import *

    #options参数可设置为default、icon与parent
    #print(messagebox.askokcancel("FishC Demo","发射核弹?"))
    #print(messagebox.askquestion("FishC Demo","买个U盘?"))
    #print(messagebox.askretrycancel("FishC Demo","启动失败,重启?"))
    #print(messagebox.askyesno("FishC Demo","你确定要格式化硬盘吗?"))
    #print(messagebox.showerror("FishC Demo","Error!!!"))
    messagebox.showinfo("Hadley","Great!!!",icon="info")

    mainloop()

    实例7:showwarning函数

    from tkinter import *

    #options参数可设置为default、icon与parent
    #print(messagebox.askokcancel("FishC Demo","发射核弹?"))
    #print(messagebox.askquestion("FishC Demo","买个U盘?"))
    #print(messagebox.askretrycancel("FishC Demo","启动失败,重启?"))
    #print(messagebox.askyesno("FishC Demo","你确定要格式化硬盘吗?"))
    #print(messagebox.showerror("FishC Demo","Error!!!"))
    #messagebox.showinfo("Hadley","Great!!!",icon="info")
    messagebox.showwarning("Hadley","Warning!!!",icon="warning")

    mainloop()

    filedialog(文本对话框)

    当应用程序需要使用打开文件或保存文件的功能时

    实例1:

    from tkinter import *

    root = Tk()

    def callback():
        #askopenfilename函数用来打开文件
        #asksaveasfilename函数用来保存文件
        fileName = filedialog.askopenfilename()
        print(fileName)

    Button(root,text="打开文件夹",command=callback).pack()

    mainloop()

    实例2:限制打开文件类型

    from tkinter import *

    root = Tk()

    def callback():
        #askopenfilename函数用来打开文件
        #asksaveasfilename函数用来保存文件
        #fileName = filedialog.askopenfilename()
        #限制打开文件类型
        fileName = filedialog.askopenfilename(filetypes=[("PNG",".png"),("GIF",".gif")])
        print(fileName)

    Button(root,text="打开文件夹",command=callback).pack()

    mainloop()

    colorchooser(颜色选择对话框)

    颜色对话框提供一个让用户选择颜色的界面

    实例1:

    from tkinter import *

    root = Tk()

    def callback():
        #colorchooser函数用于打开颜色选择对话框
        fileName = colorchooser.askcolor()
        print(fileName)

    Button(root,text="打开文件夹",command=callback).pack()

    mainloop()

    对应的RGB值及其对应的16进制值

    078 Pygame:初次见面,请大家多多关照

     

    展开全文
  • 成绩登记与查询系统App

    千次阅读 2016-04-24 14:40:29
    前段时间一个好友问我能否用AppInventor给她做一个登记学生成绩App,当然学生也可以用此App查询成绩。研究了一下,用AppInventor大致可以实现她的想法。  功能分析:App分为教师界面和学生界面,教师界面...

    前段时间一个好友问我能否用AppInventor给她做一个登记学生成绩的App,当然学生也可以用此App查询成绩。研究了一下,用AppInventor大致可以实现她的想法。

            功能分析:App分为教师界面和学生界面,教师界面添加学生基本信息,包括姓名、学号、语数英三科成绩,并保存;学生界面通过输入学生姓名和学号,可以查询自己的各科成绩。


            Step1:界面设计:



    说明:

    SplashView中两个按钮,按钮分别显示“教师”和“学生”,主要用来选择角色,当点击“教师”按钮则出现logView登录界面;


    logView为教师操作的界面,主要登记学生的基本信息(姓名和学号)以及语文、数学、英语三科成绩。“确定录入”按钮用来把信息存储在数据库中,这里依然用到了前面教程提到的新浪共享性MySql数据库;


    queryView为学生操作的界面,主要用于学生输入姓名和学号来查询成绩,“确定查询”按钮则是访问数据库数据,并把学生的成绩显示到面板上;


    对话框主要用来警告信息和提示信息;


    这里用到了两个Web组件,一个命名为webt,用来处理教师界面操作数据库插入数据,另一个命名为webs,用来处理学生界面操作数据库查询数据。


    注意:一开始将logView和queryView的可视化去掉,实现一开始不显示的效果。


    Step2:数据表设计:


    设计一个名为“studentscore”带有6个字段变量的数据表,如何创建数据表请查看我之前写的一篇文章《在SAE中创建MySql数据表》,在公众号目录AI教程中点击进去可以找到哈~~


    数据表建好之后待用~~接下来进行脚本设计


    Step3:脚本设计:

    处理教师端程序的脚本代码(php代码详细分析详见我之前写的文章《利用AppInventor实现注册功能(完整版)》

    <?php

    $name=$_POST['name'];

    $num=$_POST['num'];

    $math=$_POST['math'];

    $chinese=$_POST['chinese'];

    $english=$_POST['english'];


    $mysql = new SaeMysql();

    $sql = "SELECT * FROM `studentscore` where name='{$name}'";

    $mysql->runSql($sql); 

    $no=$mysql->affectedRows();


    if($no==0){

        $sql="INSERT INTO `studentscore` (`id` ,`name`,`num`,`math`,`chinese`,`english`)VALUES (NULL ,  '{$name}','{$num}','{$math}','{$chinese}','{$english}')";

            $mysql->runSql($sql); 

        echo 1;//用户注册

    }

    else {

    echo 2;//用户已存在

    }

    ?>

    教师端脚本主要用来将数据插入到数据表中。

    处理学生端的脚本代码如下:

    ?php


    $name=$_POST['name'];

    $num=$_POST['num'];


    $mysql = new SaeMysql();

    $sql = "SELECT * FROM `studentscore` where name='{$name}' AND num= '{$num}'";

    $mysql->runSql($sql); 

    $no=$mysql->affectedRows();


    if($no==0){

       

        echo 1;//用户或密码错误

    }

    else {

       

    $data=$mysql->getData($sql);  

    $math=$data[0]['math'];

    $chinese=$data[0]['chinese'];

    $english=$data[0]['english'];

        $resultStr="姓名:{$name}欢迎你!数学:{$math} 语文:{$chinese} 英语:{$english}";

    echo $resultStr;

    }

    ?>

    学生端脚本主要用来在数据表中查询数据!



    Step4:逻辑设计:

    1、初始化


          设置切换命令

    相信云朵们可以看懂,作用是切换用户界面

    2、教师操作界面代码设计

    (1)处理”确定录入“按钮点击事件

    设置”确定录入“按钮的点击事件,首先判断文本框里面是否为空,有一个为空则显示警告信息,接着判断语文、数学、英语文本框里面输入的是否为数字,是数字再调用teacher这个过程。teacher过程执行的功能如下:


    teacher过程主要设置头请求(前面的教程也介绍过,在此略过),设置访问网址,创建请求数据列表。

    (2)处理服务器端返回的数据(根据脚本返回的数据显示用户是否存在。


    (3)返回主界面


    点击返回按钮,返回到初始界面;

    3、学生界面代码设计

    (1)处理“确定查询”按钮点击事件


    如果用户名和学号输入都不为空,则调用student过程,该过程定义如下:


    设置请求头,访问URL以及请求数据列表。

    (2)处理服务器端返回的数据


    scoredisplay处显示成绩。

    (3)返回主界面


    Step5:测试


    打开App,点击“教师”按钮,进入到教师界面


    输入学生姓名、学号以及三科成绩,点击确定录入,稍等片刻,会出现“添加成功”的提示


    点击“返回”按钮,再次回到主界面,点击“学生”按钮进入到成绩查询界面


    我们会惊喜地发现,刚才在教师界面输入的成绩此时被查询出来了


    最后我们来看一下我们的数据表


    我们发现刚才在教师界面输入的用户信息已经被存储到数据表中了,学生界面就是访问了这个数据表直接把数据读取出来的。


    云端微课不做网络的搬运工

    坚持分享实用信息资讯和原创信息技术教程



    展开全文
  • 使用腾讯开发技术,免费资源配额,无需域名和服务器即可搭建。 小程序本身的即用即走,适合小工具的使用场景,也适合程序的开发。 项目效果截图 项目后台截图 导入数据的Excel文档成绩样本格式 部署教程:...

    项目介绍

    在这里插入图片描述

    • 本小程序包括成绩查询(学科成绩,考级成绩,竞赛成绩,其他成绩),后台成绩科目管理,后台成绩导入,后台成绩管理等功能模块!

    功能说明

    在这里插入图片描述

    技术运用

    • 项目使用微信小程序平台进行开发。
    • 使用腾讯云开发技术,免费资源配额,无需域名和服务器即可搭建。
    • 小程序本身的即用即走,适合小工具的使用场景,也适合程序的开发。

    项目效果截图在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述

    项目后台截图

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    导入数据的Excel文档成绩样本格式

    在这里插入图片描述

    部署教程:

    1 源码导入微信开发者工具

    在这里插入图片描述

    2 开通云开发环境

    • 参考微信官方文档:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/basis/getting-started.html

    • 在使用云开发能力之前,需要先开通云开发。

    • 在开发者工具的工具栏左侧,点击 “云开发” 按钮即可打开云控制台,根据提示开通云开发,并且创建一个新的云开发环境。
      在这里插入图片描述

    • 每个环境相互隔离,拥有唯一的环境 ID(拷贝此ID,后面配置用到),包含独立的数据库实例、存储空间、云函数配置等资源;

    3 云函数及配置

    • 本项目使用到了一个云函数score_cloud

    • 在云函数cloudfunctions文件夹下选择云函数score_cloud , 右键选择在终端中打开,然后执行

    • npm install –product
      在这里插入图片描述

    在这里插入图片描述

    • 打开cloudfunctions/score_cloud/comm/ccmini_config.js文件,配置环境ID和后台管理员账号和密码
      在这里插入图片描述

    本系统需要导入Excel文件,需要在云函数安装Excel内库支持,

    • 在云函数cloudfunctions文件夹下选择云函数score_cloud , 右键选择在终端中打开,然后执行
    • npm install node-xlsx

    在这里插入图片描述

    4 客户端配置

    • 打开miniprogram/app.js文件,配置环境ID
      在这里插入图片描述

    5 云函数配置

    • 在微信开发者工具-》云开发-》云函数-》对指定的函数添加环境变量
    • [服务端时间时区TZ] =>Asia/Shanghai
    • [函数内存] =>128M
    • [函数超时时间] => 60秒

    在这里插入图片描述

    6 设置图片域名信任关系

    • 进入小程序 开发管理=》开发设置=》服务器域名 =》downloadFile合法域名
    • 添加2个域名:
    • 1)你的云存储域名,格式类似:https://1234-test-pi5po-1250248.tcb.qcloud.la
    • 2)微信头像域名:https://thirdwx.qlogo.cn

    在这里插入图片描述

    7 上传云函数&指定云环境ID

    在这里插入图片描述

    至此完全部署配置完毕。

    代码地址:

    代码地址

    展开全文
  • 来自Testin测试的消息,这家移动App真机自动化测试服务的缔造者与传统人工测试的毁灭者,截至目前,已免费帮助全球移动互联网开发者测试App应用达10,155,179次,并且这一数字每一秒都在不断的增长。相对于传统的...
  • 智能课堂demo

    2017-10-06 09:40:14
    本系统由PC端、移动端和微信公众号组成,另外,我们设置了学生用户和教师用户两种不同的用户权限。学生可以加入课程学习、查看课程资源、课堂互动等操作,教师拥有设置课程、学生成绩管理、学生考勤等权限。
  • 2017最新App Store 审核指南中文版

    千次阅读 2017-08-08 08:31:51
    客户应该知道他们在下载或购买您的 app 时会得到什么,所以请确保 app 的描述、屏幕快照和预览能够准确反映 app 的核心体验,并记得不断更新,以便保持与新版本相应的最新状态。 2.3.1 请勿在 app 中包含未记录的...
  • 近日,移动支付领域中的“国家队”闪付APP迎来了自己的一岁生日,身为中国银联的掌声明珠,闪付在这一年的发展中出尽了风头。在移动支付市场环境逐渐成熟、寡头局面日益形成的格局之下,闪付的平地一声雷无疑...
  • 2017App Store 最新完整版审核指南

    千次阅读 2017-09-03 08:58:34
    近日,苹果更新了最新的App Store审核指南,新版指南本身核心内容并没有任何变化,但其规划得更有条理,且包含更多背景以便理解。...我们发现很多儿童都会大量下载各种 App。尽管家长控制功能能为儿童提供有效保
  • 11月29日消息,海底捞与阿里的合作结出硕果:搭载阿里的先进技术,海底捞打造出千人千面的超级APP,旨在为顾客提供服务升级新体验。 这款业内首个实现“千人千面”的餐饮APP在国内外都具备超前的领先性。凭借...
  • 昆明瀚云科技有限责任公司 ... ... QQ ...在国家信息安全大背景下,为企业构建安全、可靠的智慧考试系统,告别考试、微信考试、单一的WEB考试模式以及高昂的考试系统建设成本。让企业轻松、低成本构...
  • 苹果APP审核很严格,有一套他们自己的标准,我们上架经常收到拒绝条款2.1、5.1这些就是因为不符合他们的审核要求,下面是苹果APP的审核指南、对APP的基本要求,在开发APP的时候参考这个指南,争取做到符合要求一次...
  • 华为产业发展专家还分享了华为DevCloud在软件工程领域长期的探索和优秀成绩,并展示了对华为在推动工业APP发展方面所做相关支撑工作。华为产业发展专家认为,工业互联网建设已经成为国家战略,海量工业APP...
  • 见到热数据CEO白冬立是秋意正浓的一个下午。在刚刚搬进望京绿地中心国锦大厦40层热数据明亮宽敞的办公室里,这位看上去就是技术出身的CEO见到老孙的第一句话就是为中午不得不陪客户而将采访时间推后半小时而道歉...
  • App Store 审核指南

    千次阅读 2017-04-06 17:07:54
    1.1.1 诽谤或恶意内容,包括有关宗教、种族、性取向、性别或其他目标群体的引用或评论,特别是当 App 很可能对特定的个人或团体造成伤害时。专业政治讽刺和政治幽默作家通常无需遵循这一要求。 1.1.2
  • 2017年最新App Store审核指南(官方)

    千次阅读 2017-10-18 11:16:59
    简介App 正在改变世界,丰富人们的生活,并为像您一样的开发者提供前所未有的创新机会。...不管是开发新手,还是由经验丰富的程序员所组成的大型团队,我们都非常欢迎您为 App Store 开发 App,并希望能够帮助
  • 中国青年APP大赛 https://baike.baidu.com/item/%E7%AC%AC%E4%B8%89%E5%B1%8A%E4%B8%AD%E5%9B%BD%E9%9D%92%E5%B9%B4APP%E5%A4%A7%E8%B5%9B/22101278?fr=aladdin 中国(天津)工业app创新应用大赛 2019年:...
  • 2019一级建造师环球网课件下载各科学习方法 (一)公共课的学习方法 公共课的学习以老师为主,挑选自己适合的老师(后面会介绍每个老师的特点),每听一节精讲后做配套习题,整个精讲课程听完后,整套的做历年真题,把近...
  • App Store审核规范

    2018-05-04 17:36:56
    App Store 审核指南简介App 正在改变世界,丰富人们的生活,并为像您一样的开发者提供前所未有的创新机会。因此,App Store 已成长为一个激动人心且充满活力的生态系统,正为数百万的开发者和超过十亿的用户提供...
  • 只要会Java或者C或者HTML5就可以开发手机上的App,但是工业App跟手机App不一样,需要跟设备打交道,需要听懂设备、读懂设备。 传统工业应用软件往往开发难度大、开发要求高,需要开发者具备深厚的制造业经验、...
  • 苹果APP审核很严格,有一套他们自己的标准,我们上架经常收到拒绝条款2.1、5.1这些就是因为不符合他们的审核要求,下面是苹果APP的审核指南、对APP的基本要求,在开发APP的时候参考这个指南,争取做到符合要求一次...
  • 而在线音乐APP正是在数字经济的大环境下诞生的产物。例如腾讯系QQ音乐、酷狗音乐、酷我音乐;阿里系虾米音乐;网易系网易音乐等。 2021年随着虾米音乐的关停,整个在线音乐平台市场也进入新的格局——网易与腾
  • AppStore上线规则

    千次阅读 2016-08-14 20:03:01
    App Store市场营销条款(App Store Marketing Guidelines) 苹果支付身份条款(Apple Pay Identity Guidelines) 加入苹果支付许可条款(Add to Apple Wallet Guidelines) 使用Apple商标和版权的...
  • App Store 类别定义

    2017-12-20 10:49:00
    提供自动续期订阅报刊杂志内容的 App。如果您采用分期发行的策略交付内容,或为印刷版期刊提供交互式版本,那么您应当选择“报刊杂志”。 例如:报纸、杂志、其他定期期刊。 财务 可执行财务交易或协助用户展开业务...
  • 不管你是创业型公司还是小有成绩的中小型企业又或是规模已算是上市企业,不管你是想搭建一个属于自己的网站还是要完成一些线上线下的项目,现如今服务器已然成为各行各业所需标配。以前的公司想做搭建自己的网站...
  • 上线仅一个月,花粉儿就拿下了同类APP下载量第一、月交易额过千万的惊人成绩,还吸引了孙俪、董璇、黄奕、邬君梅、甘薇、国际名模吕燕等众多明星妈妈入驻。并且在2016年8月27日,花粉儿还在北京草场地以跳蚤市场的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,987
精华内容 1,994
关键字:

云成绩app免费下载