05.Python高级编程

1 ==,is的使用

  • is 是比较两个引用是否指向了同一个对象(地址引用比较)。
  • == 是比较两个对象是否相等。(比较的数值)

2 深拷贝、浅拷贝、copy.copy

2.1 浅拷贝

浅拷贝:html

  1. 拷贝的是地址引用。能够找到共同的内容
  2. 一方修改了,另外一方受影响
a = [1,2,3,4]
b = a
print(id(a)) #2540558597256
print(id(b)) #2540558597256
a.append(5)
print(a)     #[1, 2, 3, 4, 5]
print(b)     #[1, 2, 3, 4, 5]

2.2 深拷贝

深拷贝:python

  1. 深拷贝的是内容同样。地址不同。
  2. 一方修改了,另外一方不受影响
  • b = copy.deepcopy(a)算法

    b获得的内容与a的内容彻底同样,地址不同。编程

    就算a中有对象引用,b中对应的引用的对象依然是内容同样,地址不同。json

    递归拷贝缓存

    注意:
    若是是一个不可变对象(内部存储仍是不可变对象),深拷贝的结果 = 浅拷贝,地址同样多线程

2.3 copy.copy

copy.copy闭包

  1. copy.copy这个函数结果会由于是可变或者不可变致使结果不一样
  2. 只能拷贝一层。根据类型有关。若是是列表(可变类型),深拷贝。若是是元组(不可变)浅拷贝
  3. 若是里面还有嵌套的对象,浅拷贝
  • b = copy.copy(a)
import copy

a = [1,2,3,4]

#至关于深拷贝
b = copy.copy(a)

print(id(a))
print(id(b))
a.append(5)
print(a)
print(b)

print('*'*50)

a = (1,2,3,4)

#至关于浅拷贝
b = copy.copy(a)

print(id(a))
print(id(b))


----------------------------

a = [11,22,33]
b = [44,55,66]
c = [a,b]

d = copy.copy(c)

print(id(c))
print(id(d))
print(c)
print(d)

print('*'*50)

a.append(120)
#c[0].append(120)
print(c)
print(d)

print('*'*50)

a = [11,22,33]
b = [44,55,66]
c = (a,b)

d = copy.copy(c)

print(id(c))
print(id(d))
print(c)
print(d)

print('*'*50)

a.append(120)
#c[0].append(120)
print(c)
print(d)

3 属性property

3.1私有属性添加getter和setter方法

私有的内容,对外不能直接访问。
若是想对外访问,须要提供能够访问的方法,好比这里的getMoney,setMoney

调用的时候比较麻烦。
能不能像操做属性同样呢?

属性名 = property(get,set)
class Money(object):
    def __init__(self):
        self.__money = 0

    def getMoney(self):
        return self.__money

    def setMoney(self, value):
        if isinstance(value, int):
            self.__money = value
        else:
            print("error:不是整型数字")

m = Money()
print(m.getMoney())
m.setMoney(10)
print(m.getMoney())

3.2 使用property升级getter和setter方法

class Money(object):
    def __init__(self):
        self.__money = 0

    def getMoney(self):
        return self.__money

    def setMoney(self, value):
        if isinstance(value, int):
            self.__money = value
        else:
            print("error:不是整型数字")

    money = property(getMoney,setMoney)


m.money = 120       #至关于上面的m.setMoney(120)
print(m.money)      #至关于上面的m.getMoney
#或者是
print(Money.money)

3.3 使用property取代getter和setter方法

class Money(object):
    def __init__(self):
        self.__money = 0

    @property
    def money(self):
        return self.__money

    @money.setter
    def money(self, value):
        if isinstance(value, int):
            self.__money = value
        else:
            print("error:不是整型数字")

m = Money()
print(m.money)

m.money = 120

print(m.money)

4 生成器

4.1 什么是生成器

若是列表元素能够按照某种算法推算出来,那咱们是否能够在循环的过程当中不断推算出后续的元素呢?这样就没必要建立完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。app

4.2 建立生成器方法1

要建立一个生成器,有不少种方法。第一种方法很简单,只要把一个列表生成式的 [ ] 改为 ( )dom

#列表生成式
ls = [x for x in range(100)]
print(ls)

#生成器
ge = (x**2 for x in range(1000))
print(ge)
print(type(ge))

i = 0
while i<19:
    next(ge)
    i+=1
print(next(ge))

总结

生成器保存的是算法,每次调用 next(G) ,就计算出 G 的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出 StopIteration 的异常。固然,这种不断调用 next() 实在是太变态了,正确的方法是使用 for 循环,由于生成器也是可迭代对象。因此,咱们建立了一个生成器后,基本上永远不会调用 next() ,而是经过 for 循环来迭代它,而且不须要关心 StopIteration 异常。

4.3 建立生成器方法2 yield 值

yield 值

  1. 调用函数,获得一个生成器对象。这个函数没有执行
  2. next调用1获得的对象,若是遇到了yield,代码会阻塞,next的返回值就yield后的值
def fib(times):
    print('0....')
    n = 0
    a,b = 0,1
    while n<times:
        print('1....')
        yield b
        print('2....')
        a,b = b,a+b
        n+=1
    print('3....')


ge = fib(5)
print(ge)
print('*'*50)
m = 0
while m<5:
    print(next(ge))
    m+=1

4.4 send的使用

第一种和第二种,一旦生成器肯定,算法不能改变。

这里的例子,定义了变量(temp),可使用send发送参数,发给这里变量。

根据这个变量的值的不一样,能够改变算法的逻辑。

因此,这种写法的做用:在运行过程当中,能够改变算法

def gen():
    i = 0
    while i<1000:
        temp = yield i  
        if temp==True:
            #逻辑代码
            print('执行A计划')
            i+1
        else:
            #逻辑代码
            print('执行B计划')
            i+=2

myGenerator = gen()
ret = next(myGenerator)
print(ret)
#一、为当前中止的代码的左侧变量赋值
#二、生成器往下走一个行,返回yield值
ret = myGenerator.send(True)
print(ret)
ret = myGenerator.send(False)
print(ret)

4.5 模拟多任务实现方式之一:模拟协程

import time

def test1():
    while True:
        print("--王者荣耀--")
        #time.sleep(2)
        yield None

def test2():
    while True:
        print("--music--")
        yield None

def main():
    t1 = test1()
    t2 = test2()
    while True:
        t1.__next__()
        t2.__next__()

main()

4.6 总结

总结

  • 生成器是这样一个函数,它记住上一次返回时在函数体中的位置。对生成器函数的第二次(或第 n 次)调用跳转至该函数中间,而上次调用的全部局部变量都保持不变。
  • 生成器不只“记住”了它数据状态;生成器还“记住”了它在流控制构造(在命令式编程中,这种构造不仅是数据值)中的位置。

生成器的特色:

  1. 节约内存
  2. 迭代到下一次的调用时,所使用的参数都是第一次所保留下的,便是说,在整个全部函数调用的参数都是第一次所调用时保留的,而不是新建立的

5 迭代器

迭代是访问集合元素的一种方式。迭代器是一个能够记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到全部的元素被访问完结束。迭代器只能往前不会后退。

5.1 可迭代对象

以直接做用于 for 循环的数据类型有如下几种:

  • 一类是集合数据类型,如 list 、 tuple 、 dict 、 set 、 str 等;
  • 一类是 generator ,包括生成器和带 yield 的generator function。

这些能够直接做用于 for 循环的对象统称为可迭代对象: Iterable 。

5.2 迭代器

isinstance(对象,Iterable)
若是结果为True
只是表示,这些对象以使用for循环迭代遍历,可使用next

import collections


ge = (x for x in range(10))
print(isinstance(ge,collections.Iterable))  #True
print(isinstance(ge,collections.Iterator))  #True

print(next(ge))  #0

print('************************华丽的分割线************************')

ls = [x for x in range(10)]
print(isinstance(ls,collections.Iterable))  #True
print(isinstance(ls,collections.Iterator))  #False
for i in ls:
    print(i)       #0-9的数

5.3 iter()函数

  • 生成器都是 Iterator 对象,但 list 、 dict 、 str 虽然是 Iterable ,却不是 Iterator 。
  • 把 list 、 dict 、 str 等 Iterable 变成 Iterator 可使用 iter() 函数:
ls = [33,4,5,6,7,8]
it=iter(ls)
for i in range(len(ls)):
    print(next(it)) #33-8的数

总结

  • 凡是可做用于 for 循环的对象都是 Iterable 类型;
  • 凡是可做用于 next() 函数的对象都是 Iterator 类型
  • 集合数据类型如 list 、 dict 、 str 等是 Iterable 但不是 Iterator ,不过能够经过 iter() 函数得到一个 Iterator 对象。
  • 目的是在使用集合的时候,减小占用的内容。

6 闭包

6.1 函数引用

def test1():
    print("--- in test1 func----")

#调用函数
test1()

#此是ret引用函数
ret = test1

print(id(ret))     #18779264
print(id(test1))   #18779264,地址都同样,说明指向同一个内存地址

#经过引用调用函数
ret()              #--- in test1 func----

6.2 闭包

  • 在函数内部再定义一个函数,而且这个内部函数用到了外部函数的局部变量
  • 那么将这个内部函数以及用到的一些局部变量称之为闭包
def outer(num):
    print('outer...')
    def inner():
        print('num=%s'%num)
    return inner

ret = outer(100) #outer...
ret()            #num=100
ret()            #num=100
def line_conf(a, b):
    def line(x):
        return a*x + b
    return line


line1 = line_conf(2,3)
print(line1(10))   #23

line2 = line_conf(3,4)
print(line2(10))   #34

闭包的优缺点

  1. 闭包似优化了变量,原来须要类对象完成的工做,闭包也能够完成
  2. 因为闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存

7 装饰器

  • 装饰器,装饰器是闭包现象的一种体现,功能就是在运行原来功能基础上,加上一些其它功能,好比权限的验证,好比日志的记录等等。不修改原来的代码,进行功能的扩展。

7.1 简单的装饰器

def outer(func):
    print('outer...')
    def inner():
        print('inner...')
        func()
    return inner


def save():
    print('save...')


ret = outer(save) #outer
ret()             #inner...   save...
def login(func):
    def inner():
        name = input('输入用户名:')
        pwd = input('输入密码:')
        if name=='laowang' and pwd=='123':
            func()
        else:
            print('赶忙去登陆。。。。。。')
    return inner


@login
def save():
    print('save...')

save()

7.2 多个装饰器

  • 多个装饰器,按照从内往外(从下往上)前后顺序执行
  • 为了方便记忆:能够理解为先写后运行,先写后结束
def makeBold(fn):
    print('makeBold...')
    def wrapped():
        return "--1--" + fn() + "--1--"
    return wrapped

def makeItalic(fn):
    print('makeItalic...')
    def wrapped():
        return "--2--" + fn() + "--2--"
    return wrapped

@makeBold
@makeItalic
def test():
    return "hello world-test"

print(test())
#结果
#makeItalic...
#makeBold...
#--1----2--hello world-test--2----1--

7.3 装饰器功能

  1. 引入日志
  2. 函数执行时间统计
  3. 执行函数前预备处理
  4. 执行函数后清理功能
  5. 权限校验等场景
  6. 异常的处理
  7. 缓存

7.4 装饰器示例

  1. 无参数的函数
from time import ctime, sleep

def timefun(func):
    def wrappedfunc():
        print("%s called at %s"%(func.__name__, ctime()))
        func()
    return wrappedfunc

@timefun
def foo():
    print("I am foo")

foo()  #foo called at Thu Jul 20 16:03:59 2017
       #I am foo
sleep(2)
foo()
foo = timefun(foo)
#foo先做为参数赋值给func后,foo接收指向timefun返回的wrappedfunc
foo()
#调用foo(),即等价调用wrappedfunc()
#内部函数wrappedfunc被引用,因此外部函数的func变量(自由变量)并无释放
#func里保存的是原foo函数对象
  1. 被装饰的函数有参数
def timefun(func):
    def wrappedfunc(a, b):
        print("%s called at %s"%(func.__name__, ctime()))
        print(a, b)
        func(a, b)
    return wrappedfunc

@timefun
def foo(a, b):
    print(a+b)

foo(3,5)
#foo called at Thu Jul 20 16:07:48 2017
#3 5
#8
  1. 被装饰的函数有不定长参数
from time import ctime, sleep

def timefun(func):
    def wrappedfunc(*args, **kwargs):
        print(args)
        print(kwargs)
        print("%s called at %s"%(func.__name__, ctime()))
        func(*args,**kwargs)
    return wrappedfunc

@timefun
def foo(a, b, c,num):
    print(a+b+c)
    print(num)

foo(3,5,7,num=123)
#(3, 5, 7)
#{'num': 123}
#foo called at Thu Jul 20 16:11:06 2017
#15
#123
  1. 装饰器中的return
from time import ctime, sleep

def timefun_arg(pre="hello"):
    def timefun(func):
        def wrappedfunc():
            print("%s called at %s %s"%(func.__name__, ctime(), pre))
            return func()
        return wrappedfunc
    return timefun

@timefun_arg("wangcai")
def foo():
    print("I am foo")

@timefun_arg("python")
def too():
    print("I am too")

foo()
#foo called at Thu Jul 20 16:23:48 2017 wangcai
#I am foo
print('************************华丽的分割线************************')
too()
#too called at Thu Jul 20 16:23:48 2017 python
#I am too

总结:
通常状况下为了让装饰器更通用,能够有return

  1. 装饰器带参数,在原有装饰器的基础上,设置外部变量
def haha(x):
    def outer(func):
        def inner():
            if x%2==0:
                return func()+'八'
            else:
                return '八'+func()
        return inner
    return outer

@haha(1)
def laowang():
     return '老王'

print(laowang())  #八老王
  1. 类装饰器(扩展,非重点)
    装饰器函数实际上是这样一个接口约束,它必须接受一个callable对象做为参数,而后返回一个callable对象。在Python中通常callable对象都是函数,但也有例外。只要某个对象重写了__call__()方法,那么这个对象就是callable的。
class Test(object):
    def __init__(self, func):
        print("---初始化---")
        print("func name is %s"%func.__name__)
        self.__func = func
    def __call__(self):
        print("---装饰器中的功能---")
        self.__func()
#说明:
#1. 当用Test来装做装饰器对test函数进行装饰的时候,首先会建立Test的实例对象
#    而且会把test这个函数名当作参数传递到__init__方法中
#    即在__init__方法中的func变量指向了test函数体
#
#2. test函数至关于指向了用Test建立出来的实例对象
#
#3. 当在使用test()进行调用时,就至关于让这个对象(),所以会调用这个对象的__call__方法
#
#4. 为了可以在__call__方法中调用原来test指向的函数体,因此在__init__方法中就须要一个实例属性来保存这个函数体的引用
#    因此才有了self.__func = func这句代码,从而在调用__call__方法中可以调用到test以前的函数体
@Test
def test():
    print("----test---")
test()
showpy()#若是把这句话注释,从新运行程序,依然会看到"--初始化--"
运行结果以下:
---初始化---
func name is test
---装饰器中的功能---
----test---

8 python是动态语言

8.1 动态语言的定义

动态编程语言是高级程序设计语言的一个类别,在计算机科学领域已被普遍应用。它是一类 在运行时能够改变其结构的语言 :例如新的函数、对象、甚至代码能够被引进,已有的函数能够被删除或是其余结构上的变化。动态语言目前很是具备活力。例如JavaScript即是一个动态语言,除此以外如 PHP 、 Ruby 、 Python 等也都属于动态语言,而 C 、 C++ 等语言则不属于动态语言。----来自维基百科

8.2 运行的过程当中给类、对象绑定(添加)属性、方法

class mv_hero:
    def __init__(self,name,HP,MP):
        self.name = name
        self.HP = HP
        self.MP = MP
#调用,实例化
gtx = mv_hero('cjr',10,10)
print(gtx.name) #cjr
#添加一个实例属性
gtx.XP = 100
print(gtx.XP) #100
#添加一个类属性
mv_hero.xx = 99
#gtx.xx = 111
print(gtx.xx) #99

#定义一个类方法
@classmethod
def atteck(cls):
    cls.num = 100
#定义一个静态方法
@staticmethod
def test_hero():
    print('--test_hero--')

#给mv_hero类绑定类方法
mv_hero.atteck = atteck
#调用刚才绑定的类方法
mv_hero.atteck()
print(mv_hero.num)   #100

#给mv_hero类绑定静态方法
mv_hero.test_hero = test_hero
#调用刚才绑定的静态方法
mv_hero.test_hero()  #--test_hero--

import types
#定义一个实例方法
def fly(self, speed):
    print("%s在移动, 速度是 %s"%(self.name, speed))
#给mv_hero中的对象绑定实例方法
gtx.fly = types.MethodType(fly, gtx)
#调用刚才的实例方法
gtx.fly(180)  #cjr在移动, 速度是 180

9 元类

如今回到咱们的大主题上来,到底是为何你会去使用这样一种容易出错且晦涩的特性?好吧,通常来讲,你根本就用不上它!

'''
    使用type建立类
    

    type的第3个参数
    一、字符串 类名
    二、元组   父类
    三、字典      类属性
'''


'''
class Test:
    pass
'''
Test = type('Test',(),{})
print(Test)


Test = type('Test',(),{'name':'老王','age':10})

print(Test.name)


class Fu:
    def fu(self):
        print('fu...')

Zi = type('Zi',(Fu,),{})
print(Zi.__mro__)


print('***************************************华丽的分割线***************************************')

def haha(self):
    print('haha...')

def __init__(self,num):
    print('self...')
    self.num = num

@classmethod
def hehe(cls):
    print('hehe...')

Test = type('Test',(),{'name':'老王','age':10,'haha':haha,'hehe':hehe,'__init__':__init__})

t = Test(110)
t.haha()

Test.hehe()

10 垃圾回收

10.1 小整数对象池

  • 整数在程序中的使用很是普遍,Python为了优化速度,使用了小整数对象池, 避免为整数频繁申请和销毁内存空间。
  • Python 对小整数的定义是 [-5, 257) 这些整数对象是提早创建好的,不会被垃圾回收。在一个 Python 的程序中,全部位于这个范围内的整数使用的都是同一个对象.
  • 同理,单个字母也是这样的。
  • 可是当定义2个相同的字符串时,引用计数为0,触发垃圾回收

10.2 大整数对象池

  • 每个大整数,均建立一个新的对象

10.3 intern机制

python建立9个”HelloWorld”对象,让他只占用一个”HelloWorld”所占的内存空间,python中有这样一个机制——intern机制,靠引用计数去维护什么时候释放。

字符串(含有空格),不可修改,没开启intern机制,不共用对象,引用计数为0,销毁。

总结

  • 小整数[-5,257)共用对象,常驻内存
  • 单个字符共用对象,常驻内存
  • 单个单词,不可修改,默认开启intern机制,共用对象,引用计数为0,则销毁

10.4Garbage collection(GC垃圾回收)

python采用的是引用计数机制为主,标记-清除和分代收集两种机制为辅的策略

11 内建属性

12 内建函数

  1. map():map函数会根据提供的函数对指定序列作映射
map(function, sequence[, sequence, ...]) -> list
·function:是一个函数
·sequence:是一个或多个序列,取决于function须要几个参数
·返回值是一个列表
参数序列中的每个元素分别调用function函数
返回包含每次function函数返回值的list。
注意:先转成list才能print
>>>def square(x) :            # 计算平方数
...     return x ** 2

>>> map(square, [1,2,3,4,5])   # 计算列表和:1+2+3+4+5
[1, 4, 9, 16, 25]
>>> map(lambda x: x ** 2, [1, 2, 3, 4, 5])  # 使用 lambda 匿名函数
[1, 4, 9, 16, 25]
 
# 提供了两个列表,对相同位置的列表数据进行相加
>>> map(lambda x, y: x + y, [1, 3, 5, 7, 9], [2, 4, 6, 8, 10])
[3, 7, 11, 15, 19]
  1. range():
range(start, stop[, step]) -> list of integers
·start:计数从start开始。默认是从0开始。例如range(5)等价于range(0,5);
·stop:到stop结束,但不包括stop.例如:range(0,5) 是[0, 1, 2, 3, 4]没有5
·step:每次跳跃的间距,默认为1。例如:range(0,5) 等价于range(0, 5, 1)
python2中range返回列表,python3中range返回一个迭代值。若是想获得列表,可经过list函数
  1. filter():filter函数会对指定序列执行过滤操做
filter(function or None, sequence) -> list, tuple, or string
·function:接受一个参数,返回布尔值True或False
·sequence:序列能够是str,tuple,list
·返回值是一个列表
过滤出列表中的全部奇数:

def is_odd(n):
    return n % 2 == 1
 
newlist = filter(is_odd, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
print(newlist)

输出结果 :
[1, 3, 5, 7, 9]
  1. sorted()

    sort 与 sorted 区别:
    sort 是应用在 list 上的方法,sorted 能够对全部可迭代的对象进行排序操做。
    list 的 sort 方法返回的是对已经存在的列表进行操做,而内建函数 sorted 方法返回的是一个新的 list,而不是在原来的基础上进行的操做。

sorted(iterable, key=None, reverse=False) --> new sorted list
reverse默认值为False,升序排序
>>>a = [5,7,6,3,4,1,2]
>>> b = sorted(a)       # 保留原列表
>>> a 
[5, 7, 6, 3, 4, 1, 2]
>>> b
[1, 2, 3, 4, 5, 6, 7]
 
>>> L=[('b',2),('a',1),('c',3),('d',4)]
>>> sorted(L, cmp=lambda x,y:cmp(x[1],y[1]))   # 利用cmp函数
[('a', 1), ('b', 2), ('c', 3), ('d', 4)]
>>> sorted(L, key=lambda x:x[1])               # 利用key
[('a', 1), ('b', 2), ('c', 3), ('d', 4)]
 
 
>>> students = [('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)]
>>> sorted(students, key=lambda s: s[2])            # 按年龄排序
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
 
>>> sorted(students, key=lambda s: s[2], reverse=True)       # 按降序
[('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)]
  1. reduce():reduce函数会对参数序列中元素进行累积
reduce(function, sequence[, initial]) -> value
·function:该函数有两个参数
·sequence:序列能够是str,tuple,list
·initial:固定初始值

reduce依次从sequence中取一个元素,
和上一次调用function的结果作参数再次调用function。 

第一次调用function时,若是提供initial参数,
会以sequence中的第一个元素和initial做为参数调用function,
不然会以序列sequence中的前两个元素作参数调用function。 
注意function函数不能为None。

在Python3里,reduce函数已经被从全局名字空间里移除了,它如今被放置在fucntools模块里用的话要先引入:from functools import reduce

>>>def add(x, y) :            # 两数相加
...     return x + y
... 
>>> reduce(add, [1,2,3,4,5])   # 计算列表和:1+2+3+4+5
15
>>> reduce(lambda x, y: x+y, [1,2,3,4,5])  # 使用 lambda 匿名函数
15
def f(x,y):
    print('x=%s,y=%s'%(x,y))
    return x+y

#ret = functools.reduce(lambda x, y: x+y, ['a','b','c','d'],'o')
ret = functools.reduce(f, ['a','b','c','d'],'o')


运行结果:
x=o,y=a
x=oa,y=b
x=oab,y=c
x=oabc,y=d

13 functools

functools 是python2.5被引人的,一些工具函数放在此包里。

知道有这么个东西

14 模块进阶

14.1 经常使用标准库

标准库 说明
builtins 内建函数默认加载
sys 操做系统借口
functooks 经常使用的工具
json 编码和解码JSON对象
logging 记录日志、调试
multiprocessing 多进程
threading 多线程
copy 拷贝
time 时间
datetime 日期和时间
calendar 日历
hashlib 加密算法
random 生成随机数
re 字符串正则匹配
socket 标准的BSD Sockets API
shutil 文件和目录管理
glob 基于文件通配符搜索

更多标准库
http://python.usyiyi.cn/translate/python_352/library/index.html

15 编码风格

在pycharm里,Ctrl+Alt+L自动排版,排版的时候注意看一看就记住了

16 代码调试

pycharm
步骤:

  1. 设置断点
  2. shift+f9 开始调试
  3. 光标就在断点处停了。这一行没有运行的
  4. 下一行:f8
  5. 进入方法:f7
  6. 跳到下一个断点:alt+f9
  7. 进入方法,跳出这一步,shift+f8
相关文章
相关标签/搜索