精华内容
下载资源
问答
  • 系统检测到您的访问行为异常 请正确输入以下验证码,验证通过后,可继续使用经验… 然后你输入其验证码,又不说对也不说错,就卡那,点击也换不了,很是无奈 。。。。 。。。。 。。。。 这种情况持续了好久,我无法忍...

    最近在公司上网百度时经常会出现这样的提示:
    系统检测到您的访问行为异常 请正确输入以下验证码,验证通过后,可继续使用经验…
    然后你输入其验证码,又不说对也不说错,就卡那,点击也换不了,很是无奈
    。。。。
    。。。。
    。。。。
    这种情况持续了好久,我无法忍了,各种尝试了好久,有的竟然还说要改系统用户名和密码,我也是无语了,最后终发现了一个正确的方法:
    https://jingyan.baidu.com/article/b2c186c8d42b78c46ef6fffe.html
    感谢这个方法,我的把极速模式改成兼容模式,试了输入验证码直接进去了,直接进去了,直接进去了!

    展开全文
  • Java基础知识面试题(2020最新版)

    万次阅读 多人点赞 2020-02-19 12:11:27
    Java面试总结(2021优化版)已发布在个人微信公众号【技术人成长之路】,优化版首先修正了读者反馈的部分答案存在的错误,同时根据最新面试总结,删除了低频问题,添加了一些常见面试题,对文章进行了精简优化,欢迎...

    Java面试总结(2021优化版)已发布在个人微信公众号【技术人成长之路】,优化版首先修正了读者反馈的部分答案存在的错误,同时根据最新面试总结,删除了低频问题,添加了一些常见面试题,对文章进行了精简优化,欢迎大家关注!😊😊

    【技术人成长之路】,助力技术人成长!更多精彩文章第一时间在公众号发布哦!

    文章目录

    Java面试总结汇总,整理了包括Java基础知识,集合容器,并发编程,JVM,常用开源框架Spring,MyBatis,数据库,中间件等,包含了作为一个Java工程师在面试中需要用到或者可能用到的绝大部分知识。欢迎大家阅读,本人见识有限,写的博客难免有错误或者疏忽的地方,还望各位大佬指点,在此表示感激不尽。文章持续更新中…

    序号内容链接地址
    1Java基础知识面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/104390612
    2Java集合容器面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/104588551
    3Java异常面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/104390689
    4并发编程面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/104863992
    5JVM面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/104390752
    6Spring面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/104397516
    7Spring MVC面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/104397427
    8Spring Boot面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/104397299
    9Spring Cloud面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/104397367
    10MyBatis面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/101292950
    11Redis面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/103522351
    12MySQL数据库面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/104778621
    13消息中间件MQ与RabbitMQ面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/104588612
    14Dubbo面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/104390006
    15Linux面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/104588679
    16Tomcat面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/104397665
    17ZooKeeper面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/104397719
    18Netty面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/104391081
    19架构设计&分布式&数据结构与算法面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/105870730

    Java概述

    何为编程

    编程就是让计算机为解决某个问题而使用某种程序设计语言编写程序代码,并最终得到结果的过程。

    为了使计算机能够理解人的意图,人类就必须要将需解决的问题的思路、方法、和手段通过计算机能够理解的形式告诉计算机,使得计算机能够根据人的指令一步一步去工作,完成某种特定的任务。这种人和计算机之间交流的过程就是编程。

    什么是Java

    Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程 。

    jdk1.5之后的三大版本

    • Java SE(J2SE,Java 2 Platform Standard Edition,标准版)
      Java SE 以前称为 J2SE。它允许开发和部署在桌面、服务器、嵌入式环境和实时环境中使用的 Java 应用程序。Java SE 包含了支持 Java Web 服务开发的类,并为Java EE和Java ME提供基础。
    • Java EE(J2EE,Java 2 Platform Enterprise Edition,企业版)
      Java EE 以前称为 J2EE。企业版本帮助开发和部署可移植、健壮、可伸缩且安全的服务器端Java 应用程序。Java EE 是在 Java SE 的基础上构建的,它提供 Web 服务、组件模型、管理和通信 API,可以用来实现企业级的面向服务体系结构(service-oriented architecture,SOA)和 Web2.0应用程序。2018年2月,Eclipse 宣布正式将 JavaEE 更名为 JakartaEE
    • Java ME(J2ME,Java 2 Platform Micro Edition,微型版)
      Java ME 以前称为 J2ME。Java ME 为在移动设备和嵌入式设备(比如手机、PDA、电视机顶盒和打印机)上运行的应用程序提供一个健壮且灵活的环境。Java ME 包括灵活的用户界面、健壮的安全模型、许多内置的网络协议以及对可以动态下载的连网和离线应用程序的丰富支持。基于 Java ME 规范的应用程序只需编写一次,就可以用于许多设备,而且可以利用每个设备的本机功能。

    JVM、JRE和JDK的关系

    JVM
    Java Virtual Machine是Java虚拟机,Java程序需要运行在虚拟机上,不同的平台有自己的虚拟机,因此Java语言可以实现跨平台。

    JRE
    Java Runtime Environment包括Java虚拟机和Java程序所需的核心类库等。核心类库主要是java.lang包:包含了运行Java程序必不可少的系统类,如基本数据类型、基本数学函数、字符串处理、线程、异常处理类等,系统缺省加载这个包

    如果想要运行一个开发好的Java程序,计算机中只需要安装JRE即可。

    JDK
    Java Development Kit是提供给Java开发人员使用的,其中包含了Java的开发工具,也包括了JRE。所以安装了JDK,就无需再单独安装JRE了。其中的开发工具:编译工具(javac.exe),打包工具(jar.exe)等

    JVM&JRE&JDK关系图

    什么是跨平台性?原理是什么

    所谓跨平台性,是指java语言编写的程序,一次编译后,可以在多个系统平台上运行。

    实现原理:Java程序是通过java虚拟机在系统平台上运行的,只要该系统可以安装相应的java虚拟机,该系统就可以运行java程序。

    Java语言有哪些特点

    简单易学(Java语言的语法与C语言和C++语言很接近)

    面向对象(封装,继承,多态)

    平台无关性(Java虚拟机实现平台无关性)

    支持网络编程并且很方便(Java语言诞生本身就是为简化网络编程设计的)

    支持多线程(多线程机制使应用程序在同一时间并行执行多项任)

    健壮性(Java语言的强类型机制、异常处理、垃圾的自动收集等)

    安全性

    什么是字节码?采用字节码的最大好处是什么

    字节码:Java源代码经过虚拟机编译器编译后产生的文件(即扩展为.class的文件),它不面向任何特定的处理器,只面向虚拟机。

    采用字节码的好处

    Java语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。所以Java程序运行时比较高效,而且,由于字节码并不专对一种特定的机器,因此,Java程序无须重新编译便可在多种不同的计算机上运行。

    先看下java中的编译器和解释器

    Java中引入了虚拟机的概念,即在机器和编译程序之间加入了一层抽象的虚拟机器。这台虚拟的机器在任何平台上都提供给编译程序一个的共同的接口。编译程序只需要面向虚拟机,生成虚拟机能够理解的代码,然后由解释器来将虚拟机代码转换为特定系统的机器码执行。在Java中,这种供虚拟机理解的代码叫做字节码(即扩展为.class的文件),它不面向任何特定的处理器,只面向虚拟机。每一种平台的解释器是不同的,但是实现的虚拟机是相同的。Java源程序经过编译器编译后变成字节码,字节码由虚拟机解释执行,虚拟机将每一条要执行的字节码送给解释器,解释器将其翻译成特定机器上的机器码,然后在特定的机器上运行,这就是上面提到的Java的特点的编译与解释并存的解释。

    Java源代码---->编译器---->jvm可执行的Java字节码(即虚拟指令)---->jvm---->jvm中解释器----->机器可执行的二进制机器码---->程序运行。
    

    什么是Java程序的主类?应用程序和小程序的主类有何不同?

    一个程序中可以有多个类,但只能有一个类是主类。在Java应用程序中,这个主类是指包含main()方法的类。而在Java小程序中,这个主类是一个继承自系统类JApplet或Applet的子类。应用程序的主类不一定要求是public类,但小程序的主类要求必须是public类。主类是Java程序执行的入口点。

    Java应用程序与小程序之间有那些差别?

    简单说应用程序是从主线程启动(也就是main()方法)。applet小程序没有main方法,主要是嵌在浏览器页面上运行(调用init()线程或者run()来启动),嵌入浏览器这点跟flash的小游戏类似。

    Java和C++的区别

    我知道很多人没学过C++,但是面试官就是没事喜欢拿咱们Java和C++比呀!没办法!!!就算没学过C++,也要记下来!

    • 都是面向对象的语言,都支持封装、继承和多态
    • Java不提供指针来直接访问内存,程序内存更加安全
    • Java的类是单继承的,C++支持多重继承;虽然Java的类不可以多继承,但是接口可以多继承。
    • Java有自动内存管理机制,不需要程序员手动释放无用内存

    Oracle JDK 和 OpenJDK 的对比

    1. Oracle JDK版本将每三年发布一次,而OpenJDK版本每三个月发布一次;

    2. OpenJDK 是一个参考模型并且是完全开源的,而Oracle JDK是OpenJDK的一个实现,并不是完全开源的;

    3. Oracle JDK 比 OpenJDK 更稳定。OpenJDK和Oracle JDK的代码几乎相同,但Oracle JDK有更多的类和一些错误修复。因此,如果您想开发企业/商业软件,我建议您选择Oracle JDK,因为它经过了彻底的测试和稳定。某些情况下,有些人提到在使用OpenJDK 可能会遇到了许多应用程序崩溃的问题,但是,只需切换到Oracle JDK就可以解决问题;

    4. 在响应性和JVM性能方面,Oracle JDK与OpenJDK相比提供了更好的性能;

    5. Oracle JDK不会为即将发布的版本提供长期支持,用户每次都必须通过更新到最新版本获得支持来获取最新版本;

    6. Oracle JDK根据二进制代码许可协议获得许可,而OpenJDK根据GPL v2许可获得许可。

    基础语法

    数据类型

    Java有哪些数据类型

    定义:Java语言是强类型语言,对于每一种数据都定义了明确的具体的数据类型,在内存中分配了不同大小的内存空间。

    分类

    • 基本数据类型
      • 数值型
        • 整数类型(byte,short,int,long)
        • 浮点类型(float,double)
      • 字符型(char)
      • 布尔型(boolean)
    • 引用数据类型
      • 类(class)
      • 接口(interface)
      • 数组([])

    Java基本数据类型图

    switch 是否能作用在 byte 上,是否能作用在 long 上,是否能作用在 String 上

    在 Java 5 以前,switch(expr)中,expr 只能是 byte、short、char、int。从 Java5 开始,Java 中引入了枚举类型,expr 也可以是 enum 类型,从 Java 7 开始,expr 还可以是字符串(String),但是长整型(long)在目前所有的版本中都是不可以的。

    用最有效率的方法计算 2 乘以 8

    2 << 3(左移 3 位相当于乘以 2 的 3 次方,右移 3 位相当于除以 2 的 3 次方)。

    Math.round(11.5) 等于多少?Math.round(-11.5)等于多少

    Math.round(11.5)的返回值是 12,Math.round(-11.5)的返回值是-11。四舍五入的原理是在参数上加 0.5 然后进行下取整。

    float f=3.4;是否正确

    不正确。3.4 是双精度数,将双精度型(double)赋值给浮点型(float)属于下转型(down-casting,也称为窄化)会造成精度损失,因此需要强制类型转换float f =(float)3.4; 或者写成 float f =3.4F;。

    short s1 = 1; s1 = s1 + 1;有错吗?short s1 = 1; s1 += 1;有错吗

    对于 short s1 = 1; s1 = s1 + 1;由于 1 是 int 类型,因此 s1+1 运算结果也是 int型,需要强制转换类型才能赋值给 short 型。

    而 short s1 = 1; s1 += 1;可以正确编译,因为 s1+= 1;相当于 s1 = (short(s1 + 1);其中有隐含的强制类型转换。

    编码

    Java语言采用何种编码方案?有何特点?

    Java语言采用Unicode编码标准,Unicode(标准码),它为每个字符制订了一个唯一的数值,因此在任何的语言,平台,程序都可以放心的使用。

    注释

    什么Java注释

    定义:用于解释说明程序的文字

    分类

    • 单行注释
      格式: // 注释文字
    • 多行注释
      格式: /* 注释文字 */
    • 文档注释
      格式:/** 注释文字 */

    作用

    在程序中,尤其是复杂的程序中,适当地加入注释可以增加程序的可读性,有利于程序的修改、调试和交流。注释的内容在程序编译的时候会被忽视,不会产生目标代码,注释的部分不会对程序的执行结果产生任何影响。

    注意事项:多行和文档注释都不能嵌套使用。

    访问修饰符

    访问修饰符 public,private,protected,以及不写(默认)时的区别

    定义:Java中,可以使用访问修饰符来保护对类、变量、方法和构造方法的访问。Java 支持 4 种不同的访问权限。

    分类

    private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
    default (即缺省,什么也不写,不使用任何关键字): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
    protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。
    public : 对所有类可见。使用对象:类、接口、变量、方法

    访问修饰符图

    运算符

    &和&&的区别

    &运算符有两种用法:(1)按位与;(2)逻辑与。

    &&运算符是短路与运算。逻辑与跟短路与的差别是非常巨大的,虽然二者都要求运算符左右两端的布尔值都是true 整个表达式的值才是 true。&&之所以称为短路运算,是因为如果&&左边的表达式的值是 false,右边的表达式会被直接短路掉,不会进行运算。

    注意:逻辑或运算符(|)和短路或运算符(||)的差别也是如此。

    关键字

    Java 有没有 goto

    goto 是 Java 中的保留字,在目前版本的 Java 中没有使用。

    final 有什么用?

    用于修饰类、属性和方法;

    • 被final修饰的类不可以被继承
    • 被final修饰的方法不可以被重写
    • 被final修饰的变量不可以被改变,被final修饰不可变的是变量的引用,而不是引用指向的内容,引用指向的内容是可以改变的

    final finally finalize区别

    • final可以修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、修饰变量表
      示该变量是一个常量不能被重新赋值。
    • finally一般作用在try-catch代码块中,在处理异常的时候,通常我们将一定要执行的代码方法finally代码块
      中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代码。
    • finalize是一个方法,属于Object类的一个方法,而Object类是所有类的父类,该方法一般由垃圾回收器来调
      用,当我们调用System.gc() 方法的时候,由垃圾回收器调用finalize(),回收垃圾,一个对象是否可回收的
      最后判断。

    this关键字的用法

    this是自身的一个对象,代表对象本身,可以理解为:指向对象本身的一个指针。

    this的用法在java中大体可以分为3种:

    1.普通的直接引用,this相当于是指向当前对象本身。

    2.形参与成员名字重名,用this来区分:

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    

    3.引用本类的构造函数

    class Person{
        private String name;
        private int age;
        
        public Person() {
        }
     
        public Person(String name) {
            this.name = name;
        }
        public Person(String name, int age) {
            this(name);
            this.age = age;
        }
    }
    

    super关键字的用法

    super可以理解为是指向自己超(父)类对象的一个指针,而这个超类指的是离自己最近的一个父类。

    super也有三种用法:

    1.普通的直接引用

    与this类似,super相当于是指向当前对象的父类的引用,这样就可以用super.xxx来引用父类的成员。

    2.子类中的成员变量或方法与父类中的成员变量或方法同名时,用super进行区分

    class Person{
        protected String name;
     
        public Person(String name) {
            this.name = name;
        }
     
    }
     
    class Student extends Person{
        private String name;
     
        public Student(String name, String name1) {
            super(name);
            this.name = name1;
        }
     
        public void getInfo(){
            System.out.println(this.name);      //Child
            System.out.println(super.name);     //Father
        }
     
    }
    
    public class Test {
        public static void main(String[] args) {
           Student s1 = new Student("Father","Child");
           s1.getInfo();
     
        }
    }
    

    3.引用父类构造函数

    3、引用父类构造函数

    • super(参数):调用父类中的某一个构造函数(应该为构造函数中的第一条语句)。
    • this(参数):调用本类中另一种形式的构造函数(应该为构造函数中的第一条语句)。

    this与super的区别

    • super: 它引用当前对象的直接父类中的成员(用来访问直接父类中被隐藏的父类中成员数据或函数,基类与派生类中有相同成员定义时如:super.变量名 super.成员函数据名(实参)
    • this:它代表当前对象名(在程序中易产生二义性之处,应使用this来指明当前对象;如果函数的形参与类中的成员数据同名,这时需用this来指明成员变量名)
    • super()和this()类似,区别是,super()在子类中调用父类的构造方法,this()在本类内调用本类的其它构造方法。
    • super()和this()均需放在构造方法内第一行。
    • 尽管可以用this调用一个构造器,但却不能调用两个。
    • this和super不能同时出现在一个构造函数里面,因为this必然会调用其它的构造函数,其它的构造函数必然也会有super语句的存在,所以在同一个构造函数里面有相同的语句,就失去了语句的意义,编译器也不会通过。
    • this()和super()都指的是对象,所以,均不可以在static环境中使用。包括:static变量,static方法,static语句块。
    • 从本质上讲,this是一个指向本对象的指针, 然而super是一个Java关键字。

    static存在的主要意义

    static的主要意义是在于创建独立于具体对象的域变量或者方法。以致于即使没有创建对象,也能使用属性和调用方法

    static关键字还有一个比较关键的作用就是 用来形成静态代码块以优化程序性能。static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。

    为什么说static块可以用来优化程序性能,是因为它的特性:只会在类加载的时候执行一次。因此,很多时候会将一些只需要进行一次的初始化操作都放在static代码块中进行。

    static的独特之处

    1、被static修饰的变量或者方法是独立于该类的任何对象,也就是说,这些变量和方法不属于任何一个实例对象,而是被类的实例对象所共享

    怎么理解 “被类的实例对象所共享” 这句话呢?就是说,一个类的静态成员,它是属于大伙的【大伙指的是这个类的多个对象实例,我们都知道一个类可以创建多个实例!】,所有的类对象共享的,不像成员变量是自个的【自个指的是这个类的单个实例对象】…我觉得我已经讲的很通俗了,你明白了咩?

    2、在该类被第一次加载的时候,就会去加载被static修饰的部分,而且只在类第一次使用时加载并进行初始化,注意这是第一次用就要初始化,后面根据需要是可以再次赋值的。

    3、static变量值在类加载的时候分配空间,以后创建类对象的时候不会重新分配。赋值的话,是可以任意赋值的!

    4、被static修饰的变量或者方法是优先于对象存在的,也就是说当一个类加载完毕之后,即便没有创建对象,也可以去访问。

    static应用场景

    因为static是被类的实例对象所共享,因此如果某个成员变量是被所有对象所共享的,那么这个成员变量就应该定义为静态变量

    因此比较常见的static应用场景有:

    1、修饰成员变量 2、修饰成员方法 3、静态代码块 4、修饰类【只能修饰内部类也就是静态内部类】 5、静态导包

    static注意事项

    1、静态只能访问静态。 2、非静态既可以访问非静态的,也可以访问静态的。

    流程控制语句

    break ,continue ,return 的区别及作用

    break 跳出总上一层循环,不再执行循环(结束当前的循环体)

    continue 跳出本次循环,继续执行下次循环(结束正在执行的循环 进入下一个循环条件)

    return 程序返回,不再执行下面的代码(结束当前的方法 直接返回)

    在 Java 中,如何跳出当前的多重嵌套循环

    在Java中,要想跳出多重循环,可以在外面的循环语句前定义一个标号,然后在里层循环体的代码中使用带有标号的break 语句,即可跳出外层循环。例如:

    public static void main(String[] args) {
        ok:
        for (int i = 0; i < 10; i++) {
            for (int j = 0; j < 10; j++) {
                System.out.println("i=" + i + ",j=" + j);
                if (j == 5) {
                    break ok;
                }
    
            }
        }
    }
    

    面向对象

    面向对象概述

    面向对象和面向过程的区别

    面向过程

    优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;比如单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发,性能是最重要的因素。

    缺点:没有面向对象易维护、易复用、易扩展

    面向对象

    优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护

    缺点:性能比面向过程低

    面向过程是具体化的,流程化的,解决一个问题,你需要一步一步的分析,一步一步的实现。

    面向对象是模型化的,你只需抽象出一个类,这是一个封闭的盒子,在这里你拥有数据也拥有解决问题的方法。需要什么功能直接使用就可以了,不必去一步一步的实现,至于这个功能是如何实现的,管我们什么事?我们会用就可以了。

    面向对象的底层其实还是面向过程,把面向过程抽象成类,然后封装,方便我们使用的就是面向对象了。

    面向对象三大特性

    面向对象的特征有哪些方面

    面向对象的特征主要有以下几个方面

    抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么。

    封装

    封装把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法,如果属性不想被外界访问,我们大可不必提供方法给外界访问。但是如果一个类没有提供给外界访问的方法,那么这个类也没有什么意义了。

    继承

    继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承我们能够非常方便地复用以前的代码。

    关于继承如下 3 点请记住:

    1. 子类拥有父类非 private 的属性和方法。

    2. 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。

    3. 子类可以用自己的方式实现父类的方法。(以后介绍)。

    多态

    所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。

    在Java中有两种形式可以实现多态:继承(多个子类对同一方法的重写)和接口(实现接口并覆盖接口中同一方法)。

    其中Java 面向对象编程三大特性:封装 继承 多态

    封装:隐藏对象的属性和实现细节,仅对外提供公共访问方式,将变化隔离,便于使用,提高复用性和安全性。

    继承:继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承可以提高代码复用性。继承是多态的前提。

    关于继承如下 3 点请记住

    1. 子类拥有父类非 private 的属性和方法。

    2. 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。

    3. 子类可以用自己的方式实现父类的方法。

    多态性:父类或接口定义的引用变量可以指向子类或具体实现类的实例对象。提高了程序的拓展性。

    在Java中有两种形式可以实现多态:继承(多个子类对同一方法的重写)和接口(实现接口并覆盖接口中同一方法)。

    方法重载(overload)实现的是编译时的多态性(也称为前绑定),而方法重写(override)实现的是运行时的多态性(也称为后绑定)。

    一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。运行时的多态是面向对象最精髓的东西,要实现多态需要做两件事:

    • 方法重写(子类继承父类并重写父类中已有的或抽象的方法);
    • 对象造型(用父类型引用子类型对象,这样同样的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为)。

    什么是多态机制?Java语言是如何实现多态的?

    所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。

    多态分为编译时多态和运行时多态。其中编辑时多态是静态的,主要是指方法的重载,它是根据参数列表的不同来区分不同的函数,通过编辑之后会变成两个不同的函数,在运行时谈不上多态。而运行时多态是动态的,它是通过动态绑定来实现的,也就是我们所说的多态性。

    多态的实现

    Java实现多态有三个必要条件:继承、重写、向上转型。

    继承:在多态中必须存在有继承关系的子类和父类。

    重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。

    向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才能够具备技能调用父类的方法和子类的方法。

    只有满足了上述三个条件,我们才能够在同一个继承结构中使用统一的逻辑实现代码处理不同的对象,从而达到执行不同的行为。

    对于Java而言,它多态的实现机制遵循一个原则:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。

    面向对象五大基本原则是什么(可选)

    • 单一职责原则SRP(Single Responsibility Principle)
      类的功能要单一,不能包罗万象,跟杂货铺似的。
    • 开放封闭原则OCP(Open-Close Principle)
      一个模块对于拓展是开放的,对于修改是封闭的,想要增加功能热烈欢迎,想要修改,哼,一万个不乐意。
    • 里式替换原则LSP(the Liskov Substitution Principle LSP)
      子类可以替换父类出现在父类能够出现的任何地方。比如你能代表你爸去你姥姥家干活。哈哈~~
    • 依赖倒置原则DIP(the Dependency Inversion Principle DIP)
      高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象。抽象不应该依赖于具体实现,具体实现应该依赖于抽象。就是你出国要说你是中国人,而不能说你是哪个村子的。比如说中国人是抽象的,下面有具体的xx省,xx市,xx县。你要依赖的抽象是中国人,而不是你是xx村的。
    • 接口分离原则ISP(the Interface Segregation Principle ISP)
      设计时采用多个与特定客户类有关的接口比采用一个通用的接口要好。就比如一个手机拥有打电话,看视频,玩游戏等功能,把这几个功能拆分成不同的接口,比在一个接口里要好的多。

    类与接口

    抽象类和接口的对比

    抽象类是用来捕捉子类的通用特性的。接口是抽象方法的集合。

    从设计层面来说,抽象类是对类的抽象,是一种模板设计,接口是行为的抽象,是一种行为的规范。

    相同点

    • 接口和抽象类都不能实例化
    • 都位于继承的顶端,用于被其他实现或继承
    • 都包含抽象方法,其子类都必须覆写这些抽象方法

    不同点

    参数抽象类接口
    声明抽象类使用abstract关键字声明接口使用interface关键字声明
    实现子类使用extends关键字来继承抽象类。如果子类不是抽象类的话,它需要提供抽象类中所有声明的方法的实现子类使用implements关键字来实现接口。它需要提供接口中所有声明的方法的实现
    构造器抽象类可以有构造器接口不能有构造器
    访问修饰符抽象类中的方法可以是任意访问修饰符接口方法默认修饰符是public。并且不允许定义为 private 或者 protected
    多继承一个类最多只能继承一个抽象类一个类可以实现多个接口
    字段声明抽象类的字段声明可以是任意的接口的字段默认都是 static 和 final 的

    备注:Java8中接口中引入默认方法和静态方法,以此来减少抽象类和接口之间的差异。

    现在,我们可以为接口提供默认实现的方法了,并且不用强制子类来实现它。

    接口和抽象类各有优缺点,在接口和抽象类的选择上,必须遵守这样一个原则:

    • 行为模型应该总是通过接口而不是抽象类定义,所以通常是优先选用接口,尽量少用抽象类。
    • 选择抽象类的时候通常是如下情况:需要定义子类的行为,又要为子类提供通用的功能。

    普通类和抽象类有哪些区别?

    • 普通类不能包含抽象方法,抽象类可以包含抽象方法。
    • 抽象类不能直接实例化,普通类可以直接实例化。

    抽象类能使用 final 修饰吗?

    不能,定义抽象类就是让其他类继承的,如果定义为 final 该类就不能被继承,这样彼此就会产生矛盾,所以 final 不能修饰抽象类

    创建一个对象用什么关键字?对象实例与对象引用有何不同?

    new关键字,new创建对象实例(对象实例在堆内存中),对象引用指向对象实例(对象引用存放在栈内存中)。一个对象引用可以指向0个或1个对象(一根绳子可以不系气球,也可以系一个气球);一个对象可以有n个引用指向它(可以用n条绳子系住一个气球)

    变量与方法

    成员变量与局部变量的区别有哪些

    变量:在程序执行的过程中,在某个范围内其值可以发生改变的量。从本质上讲,变量其实是内存中的一小块区域

    成员变量:方法外部,类内部定义的变量

    局部变量:类的方法中的变量。

    成员变量和局部变量的区别

    作用域

    成员变量:针对整个类有效。
    局部变量:只在某个范围内有效。(一般指的就是方法,语句体内)

    存储位置

    成员变量:随着对象的创建而存在,随着对象的消失而消失,存储在堆内存中。
    局部变量:在方法被调用,或者语句被执行的时候存在,存储在栈内存中。当方法调用完,或者语句结束后,就自动释放。

    生命周期

    成员变量:随着对象的创建而存在,随着对象的消失而消失
    局部变量:当方法调用完,或者语句结束后,就自动释放。

    初始值

    成员变量:有默认初始值。

    局部变量:没有默认初始值,使用前必须赋值。

    使用原则

    在使用变量时需要遵循的原则为:就近原则
    首先在局部范围找,有就使用;接着在成员位置找。

    在Java中定义一个不做事且没有参数的构造方法的作用

    Java程序在执行子类的构造方法之前,如果没有用super()来调用父类特定的构造方法,则会调用父类中“没有参数的构造方法”。因此,如果父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用super()来调用父类中特定的构造方法,则编译时将发生错误,因为Java程序在父类中找不到没有参数的构造方法可供执行。解决办法是在父类里加上一个不做事且没有参数的构造方法。

    在调用子类构造方法之前会先调用父类没有参数的构造方法,其目的是?

    帮助子类做初始化工作。

    一个类的构造方法的作用是什么?若一个类没有声明构造方法,改程序能正确执行吗?为什么?

    主要作用是完成对类对象的初始化工作。可以执行。因为一个类即使没有声明构造方法也会有默认的不带参数的构造方法。

    构造方法有哪些特性?

    名字与类名相同;

    没有返回值,但不能用void声明构造函数;

    生成类的对象时自动执行,无需调用。

    静态变量和实例变量区别

    静态变量: 静态变量由于不属于任何实例对象,属于类的,所以在内存中只会有一份,在类的加载过程中,JVM只为静态变量分配一次内存空间。

    实例变量: 每次创建对象,都会为每个对象分配成员变量内存空间,实例变量是属于实例对象的,在内存中,创建几次对象,就有几份成员变量。

    静态变量与普通变量区别

    static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。

    还有一点就是static成员变量的初始化顺序按照定义的顺序进行初始化。

    静态方法和实例方法有何不同?

    静态方法和实例方法的区别主要体现在两个方面:

    1. 在外部调用静态方法时,可以使用"类名.方法名"的方式,也可以使用"对象名.方法名"的方式。而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象。
    2. 静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),而不允许访问实例成员变量和实例方法;实例方法则无此限制

    在一个静态方法内调用一个非静态成员为什么是非法的?

    由于静态方法可以不通过对象进行调用,因此在静态方法里,不能调用其他非静态变量,也不可以访问非静态变量成员。

    什么是方法的返回值?返回值的作用是什么?

    方法的返回值是指我们获取到的某个方法体中的代码执行后产生的结果!(前提是该方法可能产生结果)。返回值的作用:接收出结果,使得它可以用于其他的操作!

    内部类

    什么是内部类?

    在Java中,可以将一个类的定义放在另外一个类的定义内部,这就是内部类。内部类本身就是类的一个属性,与其他属性定义方式一致。

    内部类的分类有哪些

    内部类可以分为四种:成员内部类、局部内部类、匿名内部类和静态内部类

    静态内部类

    定义在类内部的静态类,就是静态内部类。

    public class Outer {
    
        private static int radius = 1;
    
        static class StaticInner {
            public void visit() {
                System.out.println("visit outer static  variable:" + radius);
            }
        }
    }
    

    静态内部类可以访问外部类所有的静态变量,而不可访问外部类的非静态变量;静态内部类的创建方式,new 外部类.静态内部类(),如下:

    Outer.StaticInner inner = new Outer.StaticInner();
    inner.visit();
    
    成员内部类

    定义在类内部,成员位置上的非静态类,就是成员内部类。

    public class Outer {
    
        private static  int radius = 1;
        private int count =2;
        
         class Inner {
            public void visit() {
                System.out.println("visit outer static  variable:" + radius);
                System.out.println("visit outer   variable:" + count);
            }
        }
    }
    

    成员内部类可以访问外部类所有的变量和方法,包括静态和非静态,私有和公有。成员内部类依赖于外部类的实例,它的创建方式外部类实例.new 内部类(),如下:

    Outer outer = new Outer();
    Outer.Inner inner = outer.new Inner();
    inner.visit();
    
    局部内部类

    定义在方法中的内部类,就是局部内部类。

    public class Outer {
    
        private  int out_a = 1;
        private static int STATIC_b = 2;
    
        public void testFunctionClass(){
            int inner_c =3;
            class Inner {
                private void fun(){
                    System.out.println(out_a);
                    System.out.println(STATIC_b);
                    System.out.println(inner_c);
                }
            }
            Inner  inner = new Inner();
            inner.fun();
        }
        public static void testStaticFunctionClass(){
            int d =3;
            class Inner {
                private void fun(){
                    // System.out.println(out_a); 编译错误,定义在静态方法中的局部类不可以访问外部类的实例变量
                    System.out.println(STATIC_b);
                    System.out.println(d);
                }
            }
            Inner  inner = new Inner();
            inner.fun();
        }
    }
    

    定义在实例方法中的局部类可以访问外部类的所有变量和方法,定义在静态方法中的局部类只能访问外部类的静态变量和方法。局部内部类的创建方式,在对应方法内,new 内部类(),如下:

     public static void testStaticFunctionClass(){
        class Inner {
        }
        Inner  inner = new Inner();
     }
    
    匿名内部类

    匿名内部类就是没有名字的内部类,日常开发中使用的比较多。

    public class Outer {
    
        private void test(final int i) {
            new Service() {
                public void method() {
                    for (int j = 0; j < i; j++) {
                        System.out.println("匿名内部类" );
                    }
                }
            }.method();
        }
     }
     //匿名内部类必须继承或实现一个已有的接口 
     interface Service{
        void method();
    }
    

    除了没有名字,匿名内部类还有以下特点:

    • 匿名内部类必须继承一个抽象类或者实现一个接口。
    • 匿名内部类不能定义任何静态成员和静态方法。
    • 当所在的方法的形参需要被匿名内部类使用时,必须声明为 final。
    • 匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。

    匿名内部类创建方式:

    new/接口{ 
      //匿名内部类实现部分
    }
    

    内部类的优点

    我们为什么要使用内部类呢?因为它有以下优点:

    • 一个内部类对象可以访问创建它的外部类对象的内容,包括私有数据!
    • 内部类不为同一包的其他类所见,具有很好的封装性;
    • 内部类有效实现了“多重继承”,优化 java 单继承的缺陷。
    • 匿名内部类可以很方便的定义回调。

    内部类有哪些应用场景

    1. 一些多算法场合
    2. 解决一些非面向对象的语句块。
    3. 适当使用内部类,使得代码更加灵活和富有扩展性。
    4. 当某个类除了它的外部类,不再被其他的类使用时。

    局部内部类和匿名内部类访问局部变量的时候,为什么变量必须要加上final?

    局部内部类和匿名内部类访问局部变量的时候,为什么变量必须要加上final呢?它内部原理是什么呢?

    先看这段代码:

    public class Outer {
    
        void outMethod(){
            final int a =10;
            class Inner {
                void innerMethod(){
                    System.out.println(a);
                }
    
            }
        }
    }
    

    以上例子,为什么要加final呢?是因为生命周期不一致, 局部变量直接存储在栈中,当方法执行结束后,非final的局部变量就被销毁。而局部内部类对局部变量的引用依然存在,如果局部内部类要调用局部变量时,就会出错。加了final,可以确保局部内部类使用的变量与外层的局部变量区分开,解决了这个问题。

    内部类相关,看程序说出运行结果

    public class Outer {
        private int age = 12;
    
        class Inner {
            private int age = 13;
            public void print() {
                int age = 14;
                System.out.println("局部变量:" + age);
                System.out.println("内部类变量:" + this.age);
                System.out.println("外部类变量:" + Outer.this.age);
            }
        }
    
        public static void main(String[] args) {
            Outer.Inner in = new Outer().new Inner();
            in.print();
        }
    
    }
    

    运行结果:

    局部变量:14
    内部类变量:13
    外部类变量:12
    

    重写与重载

    构造器(constructor)是否可被重写(override)

    构造器不能被继承,因此不能被重写,但可以被重载。

    重载(Overload)和重写(Override)的区别。重载的方法能否根据返回类型进行区分?

    方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。

    重载:发生在同一个类中,方法名相同参数列表不同(参数类型不同、个数不同、顺序不同),与方法返回值和访问修饰符无关,即重载的方法不能根据返回类型进行区分

    重写:发生在父子类中,方法名、参数列表必须相同,返回值小于等于父类,抛出的异常小于等于父类,访问修饰符大于等于父类(里氏代换原则);如果父类方法访问修饰符为private则子类中就不是重写。

    对象相等判断

    == 和 equals 的区别是什么

    == : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同一个对象。(基本数据类型 == 比较的是值,引用数据类型 == 比较的是内存地址)

    equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况:

    情况1:类没有覆盖 equals() 方法。则通过 equals() 比较该类的两个对象时,等价于通过“==”比较这两个对象。

    情况2:类覆盖了 equals() 方法。一般,我们都覆盖 equals() 方法来两个对象的内容相等;若它们的内容相等,则返回 true (即,认为这两个对象相等)。

    举个例子:

    public class test1 {
        public static void main(String[] args) {
            String a = new String("ab"); // a 为一个引用
            String b = new String("ab"); // b为另一个引用,对象的内容一样
            String aa = "ab"; // 放在常量池中
            String bb = "ab"; // 从常量池中查找
            if (aa == bb) // true
                System.out.println("aa==bb");
            if (a == b) // false,非同一对象
                System.out.println("a==b");
            if (a.equals(b)) // true
                System.out.println("aEQb");
            if (42 == 42.0) { // true
                System.out.println("true");
            }
        }
    }
    

    说明:

    • String中的equals方法是被重写过的,因为object的equals方法是比较的对象的内存地址,而String的equals方法比较的是对象的值。
    • 当创建String类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个String对象。

    hashCode 与 equals (重要)

    HashSet如何检查重复

    两个对象的 hashCode() 相同,则 equals() 也一定为 true,对吗?

    hashCode和equals方法的关系

    面试官可能会问你:“你重写过 hashcode 和 equals 么,为什么重写equals时必须重写hashCode方法?”

    hashCode()介绍

    hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode() 定义在JDK的Object.java中,这就意味着Java中的任何类都包含有hashCode()函数。

    散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码!(可以快速找到所需要的对象)

    为什么要有 hashCode

    我们以“HashSet 如何检查重复”为例子来说明为什么要有 hashCode

    当你把对象加入 HashSet 时,HashSet 会先计算对象的 hashcode 值来判断对象加入的位置,同时也会与其他已经加入的对象的 hashcode 值作比较,如果没有相符的hashcode,HashSet会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调用 equals()方法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。(摘自我的Java启蒙书《Head first java》第二版)。这样我们就大大减少了 equals 的次数,相应就大大提高了执行速度。

    hashCode()与equals()的相关规定

    如果两个对象相等,则hashcode一定也是相同的

    两个对象相等,对两个对象分别调用equals方法都返回true

    两个对象有相同的hashcode值,它们也不一定是相等的

    因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖

    hashCode() 的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 class 的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)

    对象的相等与指向他们的引用相等,两者有什么不同?

    对象的相等 比的是内存中存放的内容是否相等而 引用相等 比较的是他们指向的内存地址是否相等。

    值传递

    当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递

    是值传递。Java 语言的方法调用只支持参数的值传递。当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的属性可以在被调用过程中被改变,但对对象引用的改变是不会影响到调用者的

    为什么 Java 中只有值传递

    首先回顾一下在程序设计语言中有关将参数传递给方法(或函数)的一些专业术语。按值调用(call by value)表示方法接收的是调用者提供的值,而按引用调用(call by reference)表示方法接收的是调用者提供的变量地址。一个方法可以修改传递引用所对应的变量值,而不能修改传递值调用所对应的变量值。 它用来描述各种程序设计语言(不只是Java)中方法参数传递方式。

    Java程序设计语言总是采用按值调用。也就是说,方法得到的是所有参数值的一个拷贝,也就是说,方法不能修改传递给它的任何参数变量的内容。

    下面通过 3 个例子来给大家说明

    example 1

    public static void main(String[] args) {
        int num1 = 10;
        int num2 = 20;
    
        swap(num1, num2);
    
        System.out.println("num1 = " + num1);
        System.out.println("num2 = " + num2);
    }
    
    public static void swap(int a, int b) {
        int temp = a;
        a = b;
        b = temp;
    
        System.out.println("a = " + a);
        System.out.println("b = " + b);
    }
    

    结果

    a = 20
    b = 10
    num1 = 10
    num2 = 20
    

    解析

    img

    在swap方法中,a、b的值进行交换,并不会影响到 num1、num2。因为,a、b中的值,只是从 num1、num2 的复制过来的。也就是说,a、b相当于num1、num2 的副本,副本的内容无论怎么修改,都不会影响到原件本身。

    通过上面例子,我们已经知道了一个方法不能修改一个基本数据类型的参数,而对象引用作为参数就不一样,请看 example2.

    example 2

        public static void main(String[] args) {
            int[] arr = { 1, 2, 3, 4, 5 };
            System.out.println(arr[0]);
            change(arr);
            System.out.println(arr[0]);
        }
    
        public static void change(int[] array) {
            // 将数组的第一个元素变为0
            array[0] = 0;
        }
    

    结果

    1
    0
    

    解析

    img

    array 被初始化 arr 的拷贝也就是一个对象的引用,也就是说 array 和 arr 指向的时同一个数组对象。 因此,外部对引用对象的改变会反映到所对应的对象上。

    通过 example2 我们已经看到,实现一个改变对象参数状态的方法并不是一件难事。理由很简单,方法得到的是对象引用的拷贝,对象引用及其他的拷贝同时引用同一个对象。

    很多程序设计语言(特别是,C++和Pascal)提供了两种参数传递的方式:值调用和引用调用。有些程序员(甚至本书的作者)认为Java程序设计语言对对象采用的是引用调用,实际上,这种理解是不对的。由于这种误解具有一定的普遍性,所以下面给出一个反例来详细地阐述一下这个问题。

    example 3

    public class Test {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            Student s1 = new Student("小张");
            Student s2 = new Student("小李");
            Test.swap(s1, s2);
            System.out.println("s1:" + s1.getName());
            System.out.println("s2:" + s2.getName());
        }
    
        public static void swap(Student x, Student y) {
            Student temp = x;
            x = y;
            y = temp;
            System.out.println("x:" + x.getName());
            System.out.println("y:" + y.getName());
        }
    }
    

    结果

    x:小李
    y:小张
    s1:小张
    s2:小李
    

    解析

    交换之前:

    img

    交换之后:

    img

    通过上面两张图可以很清晰的看出: 方法并没有改变存储在变量 s1 和 s2 中的对象引用。swap方法的参数x和y被初始化为两个对象引用的拷贝,这个方法交换的是这两个拷贝

    总结

    Java程序设计语言对对象采用的不是引用调用,实际上,对象引用是按值传递的。

    下面再总结一下Java中方法参数的使用情况:

    • 一个方法不能修改一个基本数据类型的参数(即数值型或布尔型》
    • 一个方法可以改变一个对象参数的状态。
    • 一个方法不能让对象参数引用一个新的对象。

    值传递和引用传递有什么区别

    值传递:指的是在方法调用时,传递的参数是按值的拷贝传递,传递的是值的拷贝,也就是说传递后就互不相关了。

    引用传递:指的是在方法调用时,传递的参数是按引用进行传递,其实传递的引用的地址,也就是变量所对应的内存空间的地址。传递的是值的引用,也就是说传递前和传递后都指向同一个引用(也就是同一个内存空间)。

    Java包

    JDK 中常用的包有哪些

    • java.lang:这个是系统的基础类;
    • java.io:这里面是所有输入输出有关的类,比如文件操作等;
    • java.nio:为了完善 io 包中的功能,提高 io 包中性能而写的一个新包;
    • java.net:这里面是与网络有关的类;
    • java.util:这个是系统辅助类,特别是集合类;
    • java.sql:这个是数据库操作的类。

    import java和javax有什么区别

    刚开始的时候 JavaAPI 所必需的包是 java 开头的包,javax 当时只是扩展 API 包来说使用。然而随着时间的推移,javax 逐渐的扩展成为 Java API 的组成部分。但是,将扩展从 javax 包移动到 java 包将是太麻烦了,最终会破坏一堆现有的代码。因此,最终决定 javax 包将成为标准API的一部分。

    所以,实际上java和javax没有区别。这都是一个名字。

    IO流

    java 中 IO 流分为几种?

    • 按照流的流向分,可以分为输入流和输出流;
    • 按照操作单元划分,可以划分为字节流和字符流;
    • 按照流的角色划分为节点流和处理流。

    Java Io流共涉及40多个类,这些类看上去很杂乱,但实际上很有规则,而且彼此之间存在非常紧密的联系, Java I0流的40多个类都是从如下4个抽象类基类中派生出来的。

    • InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
    • OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。

    按操作方式分类结构图:

    IO-操作方式分类

    按操作对象分类结构图:

    IO-操作对象分类

    BIO,NIO,AIO 有什么区别?

    简答

    • BIO:Block IO 同步阻塞式 IO,就是我们平常使用的传统 IO,它的特点是模式简单使用方便,并发处理能力低。
    • NIO:Non IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。
    • AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,异步 IO 的操作基于事件和回调机制。

    详细回答

    • BIO (Blocking I/O): 同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。在活动连接数不是特别高(小于单机1000)的情况下,这种模型是比较不错的,可以让每一个连接专注于自己的 I/O 并且编程模型简单,也不用过多考虑系统的过载、限流等问题。线程池本身就是一个天然的漏斗,可以缓冲一些系统处理不了的连接或请求。但是,当面对十万甚至百万级连接的时候,传统的 BIO 模型是无能为力的。因此,我们需要一种更高效的 I/O 处理模型来应对更高的并发量。
    • NIO (New I/O): NIO是一种同步非阻塞的I/O模型,在Java 1.4 中引入了NIO框架,对应 java.nio 包,提供了 Channel , Selector,Buffer等抽象。NIO中的N可以理解为Non-blocking,不单纯是New。它支持面向缓冲的,基于通道的I/O操作方法。 NIO提供了与传统BIO模型中的 SocketServerSocket 相对应的 SocketChannelServerSocketChannel 两种不同的套接字通道实现,两种通道都支持阻塞和非阻塞两种模式。阻塞模式使用就像传统中的支持一样,比较简单,但是性能和可靠性都不好;非阻塞模式正好与之相反。对于低负载、低并发的应用程序,可以使用同步阻塞I/O来提升开发速率和更好的维护性;对于高负载、高并发的(网络)应用,应使用 NIO 的非阻塞模式来开发
    • AIO (Asynchronous I/O): AIO 也就是 NIO 2。在 Java 7 中引入了 NIO 的改进版 NIO 2,它是异步非阻塞的IO模型。异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。AIO 是异步IO的缩写,虽然 NIO 在网络操作中,提供了非阻塞的方法,但是 NIO 的 IO 行为还是同步的。对于 NIO 来说,我们的业务线程是在 IO 操作准备好时,得到通知,接着就由这个线程自行进行 IO 操作,IO操作本身是同步的。查阅网上相关资料,我发现就目前来说 AIO 的应用还不是很广泛,Netty 之前也尝试使用过 AIO,不过又放弃了。

    Files的常用方法都有哪些?

    • Files. exists():检测文件路径是否存在。
    • Files. createFile():创建文件。
    • Files. createDirectory():创建文件夹。
    • Files. delete():删除一个文件或目录。
    • Files. copy():复制文件。
    • Files. move():移动文件。
    • Files. size():查看文件个数。
    • Files. read():读取文件。
    • Files. write():写入文件。

    反射

    什么是反射机制?

    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

    静态编译和动态编译

    • **静态编译:**在编译时确定类型,绑定对象
    • **动态编译:**运行时确定类型,绑定对象

    反射机制优缺点

    • 优点: 运行期类型的判断,动态加载类,提高代码灵活度。
    • 缺点: 性能瓶颈:反射相当于一系列解释操作,通知 JVM 要做的事情,性能比直接的java代码要慢很多。

    反射机制的应用场景有哪些?

    反射是框架设计的灵魂。

    在我们平时的项目开发过程中,基本上很少会直接使用到反射机制,但这不能说明反射机制没有用,实际上有很多设计、开发都与反射机制有关,例如模块化的开发,通过反射去调用对应的字节码;动态代理设计模式也采用了反射机制,还有我们日常使用的 Spring/Hibernate 等框架也大量使用到了反射机制。

    举例:①我们在使用JDBC连接数据库时使用Class.forName()通过反射加载数据库的驱动程序;②Spring框架也用到很多反射机制,最经典的就是xml的配置模式。Spring 通过 XML 配置模式装载 Bean 的过程:1) 将程序内所有 XML 或 Properties 配置文件加载入内存中; 2)Java类里面解析xml或properties里面的内容,得到对应实体类的字节码字符串以及相关的属性信息; 3)使用反射机制,根据这个字符串获得某个类的Class实例; 4)动态配置实例的属性

    Java获取反射的三种方法

    1.通过new对象实现反射机制 2.通过路径实现反射机制 3.通过类名实现反射机制

    public class Student {
        private int id;
        String name;
        protected boolean sex;
        public float score;
    }
    
    public class Get {
        //获取反射机制三种方式
        public static void main(String[] args) throws ClassNotFoundException {
            //方式一(通过建立对象)
            Student stu = new Student();
            Class classobj1 = stu.getClass();
            System.out.println(classobj1.getName());
            //方式二(所在通过路径-相对路径)
            Class classobj2 = Class.forName("fanshe.Student");
            System.out.println(classobj2.getName());
            //方式三(通过类名)
            Class classobj3 = Student.class;
            System.out.println(classobj3.getName());
        }
    }
    

    网络编程

    网络编程的面试题可以查看我的这篇文章重学TCP/IP协议和三次握手四次挥手,内容不仅包括TCP/IP协议和三次握手四次挥手的知识,还包括计算机网络体系结构,HTTP协议,get请求和post请求区别,session和cookie的区别等,欢迎大家阅读。

    常用API

    String相关

    字符型常量和字符串常量的区别

    1. 形式上: 字符常量是单引号引起的一个字符 字符串常量是双引号引起的若干个字符
    2. 含义上: 字符常量相当于一个整形值(ASCII值),可以参加表达式运算 字符串常量代表一个地址值(该字符串在内存中存放位置)
    3. 占内存大小 字符常量只占两个字节 字符串常量占若干个字节(至少一个字符结束标志)

    什么是字符串常量池?

    字符串常量池位于堆内存中,专门用来存储字符串常量,可以提高内存的使用率,避免开辟多块空间存储相同的字符串,在创建字符串时 JVM 会首先检查字符串常量池,如果该字符串已经存在池中,则返回它的引用,如果不存在,则实例化一个字符串放到池中,并返回其引用。

    String 是最基本的数据类型吗

    不是。Java 中的基本数据类型只有 8 个 :byte、short、int、long、float、double、char、boolean;除了基本类型(primitive type),剩下的都是引用类型(referencetype),Java 5 以后引入的枚举类型也算是一种比较特殊的引用类型。

    这是很基础的东西,但是很多初学者却容易忽视,Java 的 8 种基本数据类型中不包括 String,基本数据类型中用来描述文本数据的是 char,但是它只能表示单个字符,比如 ‘a’,‘好’ 之类的,如果要描述一段文本,就需要用多个 char 类型的变量,也就是一个 char 类型数组,比如“你好” 就是长度为2的数组 char[] chars = {‘你’,‘好’};

    但是使用数组过于麻烦,所以就有了 String,String 底层就是一个 char 类型的数组,只是使用的时候开发者不需要直接操作底层数组,用更加简便的方式即可完成对字符串的使用。

    String有哪些特性

    • 不变性:String 是只读字符串,是一个典型的 immutable 对象,对它进行任何操作,其实都是创建一个新的对象,再把引用指向该对象。不变模式的主要作用在于当一个对象需要被多线程共享并频繁访问时,可以保证数据的一致性。

    • 常量池优化:String 对象创建之后,会在字符串常量池中进行缓存,如果下次创建同样的对象时,会直接返回缓存的引用。

    • final:使用 final 来定义 String 类,表示 String 类不能被继承,提高了系统的安全性。

    String为什么是不可变的吗?

    简单来说就是String类利用了final修饰的char类型数组存储字符,源码如下图所以:

    /** The value is used for character storage. */
    private final char value[];
    

    String真的是不可变的吗?

    我觉得如果别人问这个问题的话,回答不可变就可以了。 下面只是给大家看两个有代表性的例子:

    1) String不可变但不代表引用不可以变

    String str = "Hello";
    str = str + " World";
    System.out.println("str=" + str);
    

    结果:

    str=Hello World
    

    解析:

    实际上,原来String的内容是不变的,只是str由原来指向"Hello"的内存地址转为指向"Hello World"的内存地址而已,也就是说多开辟了一块内存区域给"Hello World"字符串。

    2) 通过反射是可以修改所谓的“不可变”对象

    // 创建字符串"Hello World", 并赋给引用s
    String s = "Hello World";
    
    System.out.println("s = " + s); // Hello World
    
    // 获取String类中的value字段
    Field valueFieldOfString = String.class.getDeclaredField("value");
    
    // 改变value属性的访问权限
    valueFieldOfString.setAccessible(true);
    
    // 获取s对象上的value属性的值
    char[] value = (char[]) valueFieldOfString.get(s);
    
    // 改变value所引用的数组中的第5个字符
    value[5] = '_';
    
    System.out.println("s = " + s); // Hello_World
    

    结果:

    s = Hello World
    s = Hello_World
    

    解析:

    用反射可以访问私有成员, 然后反射出String对象中的value属性, 进而改变通过获得的value引用改变数组的结构。但是一般我们不会这么做,这里只是简单提一下有这个东西。

    是否可以继承 String 类

    String 类是 final 类,不可以被继承。

    String str="i"与 String str=new String(“i”)一样吗?

    不一样,因为内存的分配方式不一样。String str="i"的方式,java 虚拟机会将其分配到常量池中;而 String str=new String(“i”) 则会被分到堆内存中。

    String s = new String(“xyz”);创建了几个字符串对象

    两个对象,一个是静态区的"xyz",一个是用new创建在堆上的对象。

    String str1 = "hello"; //str1指向静态区
    String str2 = new String("hello");  //str2指向堆上的对象
    String str3 = "hello";
    String str4 = new String("hello");
    System.out.println(str1.equals(str2)); //true
    System.out.println(str2.equals(str4)); //true
    System.out.println(str1 == str3); //true
    System.out.println(str1 == str2); //false
    System.out.println(str2 == str4); //false
    System.out.println(str2 == "hello"); //false
    str2 = str1;
    System.out.println(str2 == "hello"); //true
    

    如何将字符串反转?

    使用 StringBuilder 或者 stringBuffer 的 reverse() 方法。

    示例代码:

    // StringBuffer reverse
    StringBuffer stringBuffer = new StringBuffer();
    stringBuffer. append("abcdefg");
    System. out. println(stringBuffer. reverse()); // gfedcba
    // StringBuilder reverse
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder. append("abcdefg");
    System. out. println(stringBuilder. reverse()); // gfedcba
    

    数组有没有 length()方法?String 有没有 length()方法

    数组没有 length()方法 ,有 length 的属性。String 有 length()方法。JavaScript中,获得字符串的长度是通过 length 属性得到的,这一点容易和 Java 混淆。

    String 类的常用方法都有那些?

    • indexOf():返回指定字符的索引。
    • charAt():返回指定索引处的字符。
    • replace():字符串替换。
    • trim():去除字符串两端空白。
    • split():分割字符串,返回一个分割后的字符串数组。
    • getBytes():返回字符串的 byte 类型数组。
    • length():返回字符串长度。
    • toLowerCase():将字符串转成小写字母。
    • toUpperCase():将字符串转成大写字符。
    • substring():截取字符串。
    • equals():字符串比较。

    在使用 HashMap 的时候,用 String 做 key 有什么好处?

    HashMap 内部实现是通过 key 的 hashcode 来确定 value 的存储位置,因为字符串是不可变的,所以当创建字符串时,它的 hashcode 被缓存下来,不需要再次计算,所以相比于其他对象更快。

    String和StringBuffer、StringBuilder的区别是什么?String为什么是不可变的

    可变性

    String类中使用字符数组保存字符串,private final char value[],所以string对象是不可变的。StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,char[] value,这两种对象都是可变的。

    线程安全性

    String中的对象是不可变的,也就可以理解为常量,线程安全。AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。

    性能

    每次对String 类型进行改变的时候,都会生成一个新的String对象,然后将指针指向新的String 对象。StringBuffer每次都会对StringBuffer对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用StirngBuilder 相比使用StringBuffer 仅能获得10%~15% 左右的性能提升,但却要冒多线程不安全的风险。

    对于三者使用的总结

    如果要操作少量的数据用 = String

    单线程操作字符串缓冲区 下操作大量数据 = StringBuilder

    多线程操作字符串缓冲区 下操作大量数据 = StringBuffer

    Date相关

    包装类相关

    自动装箱与拆箱

    装箱:将基本类型用它们对应的引用类型包装起来;

    拆箱:将包装类型转换为基本数据类型;

    int 和 Integer 有什么区别

    Java 是一个近乎纯洁的面向对象编程语言,但是为了编程的方便还是引入了基本数据类型,但是为了能够将这些基本数据类型当成对象操作,Java 为每一个基本数据类型都引入了对应的包装类型(wrapper class),int 的包装类就是 Integer,从 Java 5 开始引入了自动装箱/拆箱机制,使得二者可以相互转换。

    Java 为每个原始类型提供了包装类型:

    原始类型: boolean,char,byte,short,int,long,float,double

    包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double

    Integer a= 127 与 Integer b = 127相等吗

    对于对象引用类型:==比较的是对象的内存地址。
    对于基本数据类型:==比较的是值。

    如果整型字面量的值在-128到127之间,那么自动装箱时不会new新的Integer对象,而是直接引用常量池中的Integer对象,超过范围 a1==b1的结果是false

    public static void main(String[] args) {
        Integer a = new Integer(3);
        Integer b = 3;  // 将3自动装箱成Integer类型
        int c = 3;
        System.out.println(a == b); // false 两个引用没有引用同一对象
        System.out.println(a == c); // true a自动拆箱成int类型再和c比较
        System.out.println(b == c); // true
    
        Integer a1 = 128;
        Integer b1 = 128;
        System.out.println(a1 == b1); // false
    
        Integer a2 = 127;
        Integer b2 = 127;
        System.out.println(a2 == b2); // true
    }
    

    常用工具类库

    单元测试

    日志

    展开全文
  • 近期在做新浪微博的抓取,需要在短时间内对新浪微博搜索页面进行访问,可是如果程序在短时间内运行几次,新浪会认为你的行为存在异常,无法获取到正常返回的页面源码。分析原因: 各大网站都有自己的保护机制,如果...

    近期在做新浪微博的抓取,需要在短时间内对新浪微博搜索页面进行访问,可是如果程序在短时间内运行几次,新浪会认为你的行为存在异常,无法获取到正常返回的页面源码。分析原因:

    各大网站都有自己的保护机制,如果发现存在进程短时间持续对网站服务器进行访问,它会认为该进程的行为有异常,许多黑客正是利用这种对网站的大规模并发访问,致使防护性较差的网站瘫痪。

    为了解决“你的行为有些异常,请输入验证码:”这个问题,我尝试使用更换ip的方法。因为既然新浪已经将当前的IP地址认为是产生异常行为的地址,所以在短时间内不会对该IP地址解封(一般2-3天以后才会解封)。所以得使用其他的IP对微博页面进行爬取。

    在实验室局域网内,ip地址是根据MAC地址固定分配。所以想要改成局域网内其他的ip地址,得需要知道局域网内有哪些ip地址可用。

    COLOR 0A
    CLS
    @ECHO Off
    Title 查询局域网内在线电脑IP
    :send
    @ECHO off&setlocal enabledelayedexpansion
    ECHO 正在获取本机的IP地址,请稍等... 
    for /f "tokens=3 skip=2 delims=: " %%i in ('nbtstat -n') do ( 
    set "IP=%%i" 
    set IP=!IP:~1,-1! 
    ECHO 本机IP为:!IP! 
    goto :next 
    )
    :next 
    for /f "delims=. tokens=1,2,3,4" %%i in ("%IP%") do set range=%%i.%%j.%%k 
    ECHO.&ECHO 正在获取本网段内的其它在线计算机名,请稍等... 
    ECHO 本网段【%range%.*】内的计算机有: 
    for /f "delims=" %%i in ('net view') do ( 
    set "var=%%i" 
    ::查询在线计算机名称 
    if "!var:~0,2!"=="\\" ( 
    set "var=!var:~2!" 
    ECHO !var! 
    ping -n 1 !var!>nul 
    )) 
    ECHO.
    ECHO 正在获取本网段内的其它在线计算机IP,请稍等... 
    for /f "skip=3 tokens=1,* delims= " %%i in ('arp -a') do ECHO IP: %%i 正在使用
    ECHO.
    ECHO 查询完毕,按任意键退出...
    pause>nul

    将以上代码存放在文本中,修改后缀名为.bat,以管理员的身份运行,可以查看当前网络中其他主机的ip地址。


    有了这些ip地址,再使用NBTSTAT -a 目的IP 得到目的IP的MAC地址。


    修改本机的MAC地址为以上查询到的MAC地址,可以使用MAC地址修改器,或者直接打开网络和共享中心,打开本地连接的属性,配置,选择“高级”,选择网络地址,填写要修改的MAC地址。


    不久,IP地址就会根据更改后的MAC地址重新分配,现在相当于两台主机共同分得网络流量使用,你可以使用这个IP重新运行程序,爬去新浪微博。

    这都是在出现“你的行为有些异常,请输入验证码:”之后的解决方法。如果想避免出现这种情况,最好是在运行程序时,不要短时间内多次运行,最好间隔一定的时间运行一次。

    本文为xyw_Eliot原创,转载请注明出处:http://blog.csdn.net/xyw_eliot/article/details/8791987

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

    万次阅读 多人点赞 2019-08-14 11:06:30
    032 异常处理:你不可能总是对的 033 异常处理:你不可能总是对的2 034 丰富的else语句及简洁的with语句 035 图形用户界面入门:EasyGui 036 类和对象:给大家介绍对象 037 类和对象:面向对象编程 038 类和对象:...

    小甲鱼老师零基础入门学习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:初次见面,请大家多多关照

     

    展开全文
  • 这时候,对于用户异常行为的防护就会变得越来越重要。但是,往往现实总是那么残酷,无法拒绝的业务需求与不得不做的安全管控,在一定的成本控制之下,对于人力的投入选择变得异常艰难。对于这样的场景,TJ给大家推荐...
  • 前端面试题(持续更新中)

    万次阅读 多人点赞 2019-11-06 17:16:33
    跨域是指a页面想获取b页面资源,如果a、b页面的协议、域名、端口、子域名不同,或是a页面为ip地址,b页面为域名地址,所进行的访问行动都是跨域的,而浏览器为了安全问题一般都限制了跨域访问,也就是不允许跨域...
  • 前端面试题

    万次阅读 多人点赞 2019-08-08 11:49:01
    同时存在于值和差值中 88 加减运算 88 什么是同源策略? 88 为什么不能定义1px左右的div容器?  89 结果是什么? 89 输出结果 89 计算字符串字节数: 90 结果是: 90 声明对象,添加属性,输出属性...
  • Redis面试题集

    千次阅读 多人点赞 2019-09-16 10:19:31
    1、共同好友 2、利用唯一性,统计访问网站的所有独立ip 3、好友推荐时,根据tag求交集,大于某个阈值就可以推荐 Sorted Set(有序集合) 将Set中的元素增加一个权重参数score,元素按score有序排列 数据插入集合...
  • adb命令大全

    千次阅读 多人点赞 2019-06-03 22:19:07
    4.9.9 查看设备 IP 地址 4.9.10 查看设备 Mac 地址 4.9.11 查看设备 CPU 信息 4.9.12 查看设备内存信息 4.9.13 查看设备更多硬件与系统属性 4.10 修改设置 4.10.1 修改分辨率 4.10.2 修改屏幕密度 4.10.3 修改显示...
  • 测试开发笔记

    万次阅读 多人点赞 2019-11-14 17:11:58
    访问SVN 157 进销存项目 158 2011年10月20日 158 进销存项目总结 160 测试需求分析 160 1、定义测试范围 160 2、建立需求项 160 3、细化需求项 162 4、需求覆盖率分析 164 课前复习: 164 判定表 166 3.1....
  • JAVA面试笔记

    千次阅读 多人点赞 2019-03-07 17:52:40
    ​ 所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了如下几个表示传播行为的常量: ...
  • 软件测试面试题汇总

    万次阅读 多人点赞 2018-09-27 12:31:09
    2、我现在有个程序,发现在Windows上运行得很慢,怎么判别是程序存在问题还是软硬件系统存在问题?   5 3、测试的策略有哪些? ................................................................................
  • C#基础教程-c#实例教程,适合初学者

    万次阅读 多人点赞 2016-08-22 11:13:24
     增加安全:C#语言不支持指针,一切对内存的访问都必须通过对象的引用变量来实现,只允许访问内存中允许访问的部分,这就防止病毒程序使用非法指针访问私有成员。也避免指针的误操作产生的错误。CLR执行中间语言...
  • Tomcat面试题+http面试题+Nginx面试题+常见面试题

    千次阅读 多人点赞 2019-12-12 15:04:43
    ip_hash:每个请求按照IP的哈希结果分配,使来自同一个IP的访客固定访问后端服务器,可以有效的解决动态网页存在的session共享问题。 fair:比weight、ip_hash更加智能的负载均衡算法,fair算法可以根据页面的大小...
  • 史上最全面Java面试汇总(面试题+答案)

    万次阅读 多人点赞 2018-07-06 14:09:25
    重写:发生在父子类中,方法名、参数列表必须相同,返回值小于等于父类,抛出的异常小于等于父类,访问修饰符大于等于父类;如果父类方法访问修饰符为private则子类中就不是重写。 4.构造器Constructor是否可被...
  • a:通过消息队列每5分钟上报一次当前服务的ip和端口到统一的解析平台 b:通过消息队列实现微信支付预约成功后 推送成功消息到门店大堂叫号机去更新叫号列表 24、E-R图 实体-联系图(Entity Relationship Diagram) ...
  • 【腾讯云】服务违规封禁提醒 解决方法来自腾讯客服 ...禁止外网访问 Redis 修改 redis.conf 文件,添加或修改 bind 127.0.0.1 使得 Redis 服务只在当前主机可用 您可以按照修复建议处理下 ,处理完后更改下服务器密码
  • 【JAVA面试】java面试题整理(3)

    千次阅读 2018-10-28 12:50:13
    在使用MySQL的过程中对MyISAM和InnoDB这两个概念存在了些疑问,到底两者引擎有何分别一直是存在我心中的疑问。为了解开这个谜题,搜寻了网络,找到了如下信息:   MyISAM是MySQL的默认数据库引擎(5.5版之前...
  • Python爬虫实战

    千次阅读 2017-05-11 10:24:03
    多线程抓取的作用是提高抓取效率,但是现在主流的搜索平台,在关键数据上都做了防抓取的工作,过快的抓取频率很容易导致当前IP的流量被视为异常流量,最坏的结果是IP被列入网站的黑名单,被限流或禁止访问一段时间...
  • 分布式服务框架

    千次阅读 2016-01-29 10:42:11
    memcache 存储支持的数据格式也是灵活多样的,通过对象的序列化机制,可以将更高层抽象的对象转换成为二进制数据,存储在缓存服务器中,当前端应用需要时,又可以通过二进制内容反序列化,将数据还原成原有对象。...
  • 网站会用 cookie 跟踪你的访问过程,如果发现了爬虫异常行为就会中断你的访问,比如特别快速地填写表单,或者浏览大量页面。虽然这些行为可以通过关闭并重新连接或者改变 IP 地址来伪装,但是如果 cookie 暴露了你的...
  • 无线传感器网络复习大纲

    千次阅读 多人点赞 2019-04-30 10:31:40
     ②、从节点的物理层集成化程度、成本的角度来考虑,虽然当前的CMOS工艺已经成为主流,但是对大电感的集成化还是一个非常大的挑战,由于无线传感器网络是一种面向应用的网络,FCC组织给出2.4GHz的一个全球ISM频段。...
  • 今天在学习selenium的时候尝试了一下登陆知乎,本来我觉着我这不就是通过浏览器进行登陆的,肯定没有问题,然而,打脸来的太快了,提示了求情信息异常,好吧,我很菜,在通过百度过后解决了问题(菜鸡一枚,不会就...
  • 行为分析】(二)前端埋点实现及原理分析

    万次阅读 多人点赞 2017-07-23 08:57:05
    正如在宏观介绍的博客中写到的,做用户行为分析的方式有“前端埋点”和“后端埋点”的区分,真好今天敲了一个坤哥整理的“前端埋点”的程序,理解了之后结合demo来简单讲解“前端埋点”如何做。  前端埋点原理图: ...
  • TCP/IP网络协议详解

    千次阅读 2021-03-30 18:54:59
    一、 计算机网络体系结构分层 一图看完本文 计算机网络体系结构...从字面意义上讲,有人可能会认为 TCP/IP 是指 TCP 和 IP 两种协议。实际生活当中有时也确实就是指这两种协议。然而在很多情况下,它只是利用 I.
  • Rancher容器网络-Floating IP解决方案

    千次阅读 2016-12-29 09:56:33
    Rancher网络使用中遇到的问题首先,在为某一个stack编写Rancher catalog的时候,假设在docker-compose里指定了ports A:B,那么,cattle在调度的时将首先过滤掉所有已经占用了port A...另一方面,客户端通过主机的IP地址
  • C++ 面试题大全

    千次阅读 多人点赞 2017-05-31 09:42:54
    更糟的是,父类通常至少定义了子类的部分行为,父类的任何改变都可能影响子类的行为。如果继承下来的实现不适合解决新的问题,则父类必须重写或被其他更适合的类替换。这种依赖关系限制了灵活性并最终限制了复用性。...
  • 深信服上网行为管理的学习笔记,由于AC软件版本更新相关特性可能变动,仅供参考哈。。 权威内容请访问深信服官方社区:https://bbs.sangfor.com.cn/ 目录 默认IP 接口保留地址 路由模式 网桥模式 旁路...
  • 本文主要分享作阿里云搭建LNMP环境及实现PHP自定义网站IP访问,同时详细介绍走过的坑,还发了朋友圈求助大家,再次感谢。本来想重新设置一个专栏分享基于阿里云的PHP网站搭建,但考虑到搞安全的也会面临各种靶场及...
  • TCP/IP协议学习总结

    万次阅读 多人点赞 2021-10-28 11:37:44
    TCP/IP协议学习总结网络基础知识连接人与人的计算机网络协议随处可见的协议协议的必要性计算机中的协议分组交换协议协议分层与OSI参考模型协议的分层OSI参考模型OSI协议与OSI参考模型OSI参考模型中各个分层的作用7层...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 36,813
精华内容 14,725
关键字:

当前访问的ip存在异常行为