啥叫佩奇?啥叫类?啥叫面向对象?后面两个问题以前在大学里“祖传谭浩强”的时候我经常会有所疑问。老师说着一堆什么public, private,我都是一脸懵逼,啥叫私有?为啥要私有?然后就神游天外了……

后来由于一直接触数据挖掘类的内容,写得那根本不是程序,都是脚本,而且基本上也用不到类这种东西。直到前几天,在一个项目中,我需要对8个分类分别应用同一个模型,我才又回想起了被类支配的恐惧。本文即劫后余生的产物,没有网络上哪些看到想吐的Foo, Bar,只有最通俗易懂的语言。

P1 起因

正如上文所说,我需要对8个分类分别应用同一个模型,这8个分类的数据不同,但扔进模型并跑一跑的过程是相似的,除此之外,我可能还需要对每个模型进行一些调参,每个模型还需要在不同的时间段上进行验证(即传入模型的数据不同)。

如果我只用一个脚本的话,面临的问题就是,我需要分别记录这8组参数,不同分类之间的运行调试可能会互相干扰,修改代码也不方便。那有没有方便的方法呢?有啊,懒人福音——类(class)。

注意,本文由于作者水平不行,只说基础内容。

P2 什么是类

如果你要我说正经的解释,抱歉,我完全说不出来,毕竟我是野路子码农……但我可以给出一个稍微通俗一些的解释,类是一个容器,里面可以存放数据和操作。

举个例子的话,银行账户可以当成一个类。类里存放有数据,比如名字和金额;类里也存放有操作(即函数),比如余额查询、存钱、取钱。类可以实例化,就像银行账户可以为张三开立,也可以为王二狗开立,大多数情况下,他们之间互不影响。

P3 开始玩类

好了,假设我们就把银行账户当成一个类吧:

  1. class Bank_Account:
  2. pass

我们创建了一个叫Bank_Account的类,pass代表我们什么都没做。这一步就像银行柜员拿出了一张空卡。可是卡里总要有点内容吧?

  1. class Bank_Account:
  2. def __init__(self):
  3. self.name = None # 户主的名字
  4. self.balance = 0 # 账户余额

这里我们创建了第一个函数__init__(),这是用来实例化的时候初始化用的,这里我们记录了账户户主的名字,账户的余额。初始化就像拿出一张新卡,新卡没有户主的名字,账户余额是0,很好理解吧?

注意这里的self关键字,这代表了实例化之后东西,听起来可能比较拗口。比如这卡发给张三,那么self就代表张三的账户,发给王二狗,那self就代表王二狗的账户。

柜员把这张卡签发给了张三,但注意,此时里面毛都没有。

  1. john_san = Bank_Account() # 这个账户签发给了张三
  2. print(john_san.name) # 初始时没有名字
  3. print(john_san.balance) # 初始时余额为0

返回结果

  1. None
  2. 0

self.name这种前面带self.的我们称之为实例变量,它根据每个实例的不同而不同,我们可以分别设定,实例之间互不影响:

  1. john_san = Bank_Account() # 签发给张三的账户
  2. john_san.name = 'John San'
  3.  
  4. wanna_go = Bank_Account() # 签发给王二狗的账户
  5. wanna_go.name = 'Wanna Go'
  6.  
  7. print(john_san.name)
  8. print(wanna_go.name)

john_san和wanna_go虽然同属一个类,但他们的实例变量相互独立,互不影响(这一点很重要)。返回结果:

  1. John San
  2. Wanna Go

好了,张三和王二狗的账户都开好了,可这卡啥事都干不了,我们需要添加一些操作(函数),我们加个存钱函数吧:

  1. class Bank_Account:
  2. def __init__(self):
  3. self.name = None # 户主的名字
  4. self.balance = 0 # 账户余额
  5.  
  6. def deposit(self, amt):
  7. # self依然代表实例本身,amt代表存入金额
  8. self.balance = self.balance + amt

存钱函数一共有2个参数,一个是self,即代表实例本身,这个参数在实际调用函数时不必传入,另一个是真正的参数amt,即存入金额。我们调用deposit函数时只需要传入amt即可,self不用管它。

  1. john_san = Bank_Account()
  2. john_san.name = 'John San'
  3.  
  4. john_san.deposit(100) # 只需传入amt参数,张三存了100
  5. print(john_san.balance)

返回结果:

  1. 100

我们可以注意到,类中我们定义的这个deposit函数和一般正经的函数还不太一样,别人都有return,它却没有。但别慌,这很正常,因为参数参与的运算结果已经传回给了self.balance,不需要再return了。

P4 变量其实是个传参用的记事本

好了,有了存钱的操作之后,我们还想加入一个余额查询的操作,虽然我们可以调用john_san.balance直接查看,但我们还是希望显得像正经人一些。

  1. class Bank_Account:
  2. def __init__(self):
  3. self.name = None # 户主的名字
  4. self.balance = 0 # 账户余额
  5.  
  6. def deposit(self, amt):
  7. # self依然代表实例本身,amt代表存入金额
  8. self.balance = self.balance + amt
  9.  
  10. def check_balance(self):
  11. # 这个函数实际调用时不需要任何参数
  12. print(self.balance)

注意,此处check_balance这个函数只有类内部数据的传递(self.balance),查个账户余额也不需要传入任何外部参数,所以实际调用时无需任何参数。我们重新让张三存入100,再看看结果。

  1. john_san = Bank_Account()
  2. john_san.name = 'John San'
  3.  
  4. john_san.deposit(100)
  5. john_san.check_balance()

返回结果

  1. 100

和之前一模一样,成功啦。这里我们可以看到,类内我们定义的这些实例变量(就是那些self.xxx们)其实就像个记事本,它们主要的作用就是在函数间传递数据。就像之前我们所看到的,self.balance首先在__init__函数中记录了账户余额的数据(也就是0);之后张三调用deposit函数存钱时,self.balance又记录了存钱之后的账户余额数据;而后我们在用check_balance查询账户余额时,self.balance又将之前记录的数据传给这个函数。因此,在类的每个实例中(张三的账户、王二狗的账户),实例变量(self.balance)都扮演着一个内部的记事本的角色。它记录了一些内部数据(账户余额),并在函数中相互传递(存钱 -> 查询余额)。

因此,每当我们内部需要传递一组数据时,我们就需要在__inti__下方登记一个实例变量,这就相当于我们领了一本记事本,用这个本子专门记录某一组内部数据,比如像下面这样:

  1. class Bank_Account:
  2. def __init__(self):
  3. self.name = None # 户主的名字
  4. self.balance = 0 # 账户余额
  5. self.nickname = None # 新建一个实例变量用于记录昵称
  6.  
  7. def deposit(self, amt):
  8. # self依然代表实例本身,amt代表存入金额
  9. self.balance = self.balance + amt
  10.  
  11. def check_balance(self):
  12. # 这个函数实际调用时不需要任何参数
  13. print(self.balance)
  14.  
  15. def set_nickname(self, name):
  16. # 设定昵称
  17. self.nickname = name
  18.  
  19. def get_nickname(self):
  20. # 显示昵称
  21. print(self.nickname) # self.nickname在函数set_nickname与get_nickname中传递昵称数据

P5 公有还是私有

张三有个仇人李四,他总想对张三做点什么,李四知道张三开了个账户之后,就想通过修改张三的账户余额来使其蒙受损失(可以说很科幻了)。我们来看看我们之前定义的这个类,以及它的变量和函数:

  1. # 变量
  2. self.name
  3. self.balance
  4. self.nickname
  5. # 函数
  6. __init__(self)
  7. deposit(self, amt)
  8. check_balance(self)
  9. set_nickname(self, name)
  10. get_nickname(self)

我们看到除了__init__前后有下划线,长得比较奇怪以外,其他的都跟平时我们定义的变量和函数差不多,这些都是公有的(public),啥意思呢?

意思就是变量可以直接指定值:

  1. john_san.balance = 2000

方法,或者说函数可以直接调用:

  1. john_san.deposit(-1000)

对张三的账户来说,这是件非常危险的事情!任何人都可以直接修改他的账户余额,包括李四。我们必须把某些重要的东西保护起来,不让外部随便修改。

一个非常简单的方法就是把self.balance变成私有变量(private),怎么变?变量名字前面加两道下划线:

  1. class Bank_Account:
  2. def __init__(self):
  3. self.name = None # 户主的名字
  4. self.__balance = 0 # 账户余额(前面加两道下划线变为私有变量)
  5. self.nickname = None # 昵称
  6.  
  7. def deposit(self, amt):
  8. # self依然代表实例本身,amt代表存入金额
  9. self.__balance = self.__balance + amt
  10.  
  11. def check_balance(self):
  12. # 这个函数实际调用时不需要任何参数
  13. print(self.__balance)
  14.  
  15. def set_nickname(self, name):
  16. # 设定昵称
  17. self.nickname = name
  18.  
  19. def get_nickname(self):
  20. # 显示昵称
  21. print(self.nickname) # self.nickname在函数set_nickname与get_nickname中传递昵称数据

账户余额balance变为私有变量之后,我们就无法从外部接触到这个变量了,不信你可以试试:

  1. john_san = Bank_Account()
  2. john_san.name = 'John San'
  3.  
  4. john_san.__balance

会抛出一个AttributeError,说Bank_Account这个类没有__balance这个属性。这样一来账户余额再也无法从外部直接查看和修改了,只能通过类内部的函数间接查看和修改(比如调用deposit或check_balance)。这样一来,我们有效地保护了账户余额这个变量不会被乱改或者错改。

  1. john_san = Bank_Account()
  2. john_san.name = 'John San'
  3.  
  4. john_san.deposit(250)
  5. john_san.check_balance()

返回结果:

  1. 250

在实际应用的过程中,我们可能会遇到这样一种情况:我跑了一个分类器,我希望得到训练时的概率最大值(比如0.9),而我又希望后续预测过程中以这一概率的50%作为阈值。假设我们将最大概率记录在self.max_proba中,我们有可能会因为误操作或者贪图方便,直接指定了max_proba的值,当我们回头又需要训练时得到的max_proba时,我们会发现它已经被我们自己更改了,得从头开始训练模型,而这可能非常耗时。因此,若我们把max_proba变为私有变量,那就不会存在这一问题了,因为我们无法从外部直接修改它,也就避免了不必要的麻烦。

当然,函数也是类似的道理,假设我们定义了一个新的函数bonus,它的作用是奖励账户余额的1%。我们在deposit函数中调用它,也就意味着每次用户存款之后,银行都会给予他(她)账户余额1%的金额作为奖励(这银行吃枣药丸……)。

  1. class Bank_Account:
  2. def __init__(self):
  3. self.name = None # 户主的名字
  4. self.__balance = 0 # 账户余额(前面加两道下划线变为私有变量)
  5. self.nickname = None # 昵称
  6.  
  7. def deposit(self, amt):
  8. # self依然代表实例本身,amt代表存入金额
  9. self.__balance = self.__balance + amt
  10. self.bonus() # 每次存款会获得一次奖励
  11.  
  12. def check_balance(self):
  13. # 这个函数实际调用时不需要任何参数
  14. print(self.__balance)
  15.  
  16. def set_nickname(self, name):
  17. # 设定昵称
  18. self.nickname = name
  19.  
  20. def get_nickname(self):
  21. # 显示昵称
  22. print(self.nickname) # self.nickname在函数set_nickname与get_nickname中传递昵称数据
  23.  
  24. def bonus(self):
  25. # 奖励账户余额1%的金额
  26. self.__balance = 1.01 * self.__balance

我们存款试试:

  1. john_san = Bank_Account()
  2. john_san.name = 'John San'
  3.  
  4. john_san.deposit(250)
  5. john_san.check_balance()

返回结果:

  1. 252.5

然而,这又存在一个安全性问题,如果张三是内心险恶之人,他可以多次反复调用bonus函数,不断获得奖励而根本不用存钱,这家银行岂不是药丸得更快了?如何避免这一点呢?我们将bonus转为私有函数,同样也是前面加两根下划线:

  1. class Bank_Account:
  2. def __init__(self):
  3. self.name = None # 户主的名字
  4. self.__balance = 0 # 账户余额(前面加两道下划线变为私有变量)
  5. self.nickname = None # 昵称
  6.  
  7. def deposit(self, amt):
  8. # self依然代表实例本身,amt代表存入金额
  9. self.__balance = self.__balance + amt
  10. self.__bonus() # 类内依然可以调用,但类外就不行了
  11.  
  12. def check_balance(self):
  13. # 这个函数实际调用时不需要任何参数
  14. print(self.__balance)
  15.  
  16. def set_nickname(self, name):
  17. # 设定昵称
  18. self.nickname = name
  19.  
  20. def get_nickname(self):
  21. # 显示昵称
  22. print(self.nickname) # self.nickname在函数set_nickname与get_nickname中传递昵称数据
  23.  
  24. def __bonus(self):
  25. # 现在它变成了私有函数
  26. self.__balance = 1.01 * self.__balance

现在我们把bonus变成了私有函数,只有类内的其他函数能用调用它,而我们在外部是无法调用的,不信可以试试:

  1. john_san = Bank_Account()
  2. john_san.name = 'John San'
  3.  
  4. john_san.__bonus()

同样会抛出一个AttributeError,说Bank_Account这个类没有__bonus这个属性。这样一来,我们就避免了张三利用漏洞作弊的可能。

除此之外,私有化也能让你的自动补全变得更为简洁,我们可能有很多预处理函数或者中间变量,它们的作用只是完成一些中间步骤,并将结果传入最终处理的函数中,我们可能并不希望按下Tab时会看到这些东西,会有些混乱。那么在确定没问题的情况下,我们可以将它们全部私有化,那它们也不会出现在自动补全里了。

P6 类变量

之前我们说的那些带self.的变量都称为实例变量,那我们也会听到类变量,类变量又是什么鬼呢?

  1. class Bank_Account:
  2. bank_name = 'Bank of XXX' # 类变量,银行的名字
  3.  
  4. def __init__(self):
  5. self.name = None # 户主的名字
  6. self.__balance = 0 # 账户余额(前面加两道下划线变为私有变量)
  7. self.nickname = None # 昵称
  8.  
  9. def deposit(self, amt):
  10. # self依然代表实例本身,amt代表存入金额
  11. self.__balance = self.__balance + amt
  12. self.__bonus() # 类内依然可以调用,但类外就不行了
  13.  
  14. def check_balance(self):
  15. # 这个函数实际调用时不需要任何参数
  16. print(self.__balance)
  17.  
  18. def set_nickname(self, name):
  19. # 设定昵称
  20. self.nickname = name
  21.  
  22. def get_nickname(self):
  23. # 显示昵称
  24. print(self.nickname) # self.nickname在函数set_nickname与get_nickname中传递昵称数据
  25.  
  26. def __bonus(self):
  27. # 现在它变成了私有函数
  28. self.__balance = 1.01 * self.__balance

我们在__init__()函数之前加了一个变量,这个变量的名字前面没有self.,它是直接属于Bank_Account这个类的变量,在这里就是银行的名字。每个签发的账户都会有这个属性,而且一模一样。这和实例变量的初始化并没有什么区别。

  1. john_san = Bank_Account()
  2. john_san.name = 'John San'
  3.  
  4. wanna_go = Bank_Account()
  5. wanna_go.name = 'Wanna Go'
  6.  
  7. print(john_san.bank_name)
  8. print(wanna_go.bank_name)

返回结果:

  1. Bank of XXX
  2. Bank of XXX

现在银行突然改名字了,我们看一下结果:

  1. Bank_Account.bank_name = 'Bank of OOO' # 注意,这里是设定类,而不是某个实例
  2.  
  3. print(john_san.bank_name)
  4. print(wanna_go.bank_name)

返回结果:

  1. Bank of OOO
  2. Bank of OOO

这就相当省心了,我们无需一个一个实例地去改,直接改类变量的值就行了。我们也可以试试修改实例变量,例如:

  1. Bank_Account.nickname = 'Leese'

你会发现什么都没有发生……

当然,还要注意的是,如果某个实例已经改过类变量了,那么同意更改类变量的时候,这个实例上就不会发生任何事情。比如李四这厮把自己账户的银行名称私自改成李四666:

  1. john_san = Bank_Account()
  2. john_san.name = 'John San'
  3.  
  4. wanna_go = Bank_Account()
  5. wanna_go.name = 'Wanna Go'
  6.  
  7. leese = Bank_Account()
  8. leese.name = 'Leese'
  9. leese.bank_name = 'Leese 666'
  10.  
  11. print(john_san.bank_name)
  12. print(wanna_go.bank_name)
  13. print(leese.bank_name)

返回结果:

  1. Bank of XXX
  2. Bank of XXX
  3. Leese 666

现在银行改名字了:

  1. Bank_Account.bank_name = 'Bank of OOO'
  2.  
  3. print(john_san.bank_name)
  4. print(wanna_go.bank_name)
  5. print(leese.bank_name) # 神马都没发生

返回结果:

  1. Bank of OOO
  2. Bank of OOO
  3. Leese 666

你看,李四账户的银行名称并没有发生变化,李四真的6。

P7 setattr与getattr

这一段可能说起来就没那么通俗了,正常情况下,我们在类内可以用赋值语句直接指定变量的值,可以方便的更改和查看变量。那么如果现在我有一个循环,生成了12个变量需要储存,要怎么办呢?我一个个手写?如果是2000个变量呢?那岂不是手写到天明……

我们可能会想到用循环,那么怎么在类内循环调用不同名字的变量呢?eval()是行不通的,我们需要使用setattr和getattr(attr就是attribute的意思)。

举个例子,在下面这个类中,我们初始化了12个实例变量,代表12个月,它们的名字是mon_1, mon_2, ... , mon_12,我们将它们的值对应设成1~12。

  1. class Months:
  2. def __init__(self):
  3. for i in range(12):
  4. setattr(self, 'mon_%i'%(i+1), i+1)
  5.  
  6. def print_months(self):
  7. for i in range(12):
  8. print(getattr(self, 'mon_%i'%(i+1)))

我们试试:

  1. mm = Months()
  2. mm.print_months()

返回结果:

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12

这样我们就能通过循环和%占位符生成、修改与查看多个变量了。我们通过setattr来修改这些变量,而用getattr来查看这些变量。其中setattr有3个参数,第一个一般就是self,表示实例本身,第二个是个字符串,代表变量的名字,第三个代表设定的值。举例来说:

  1. setattr(self, 'new_1', 3)

其实等价于:

  1. self.new_1 = 3

而getattr有2个参数,第一个一般就是self,表示实例本身,第二个是个字符串,代表变量的名字。

  1. getattr(self, 'new_1')

其实等价于:

  1. self.new_1

实际应用过程中,我们可能在k折中训练了k个模型,我们想暂时保存起来,之后预测的时候再调用。我们可以通过循环和setattr来将模型保存在实例变量中,然后在需要的时候通过getattr来调用这些模型。

P8 类的优势

假设现在我们有四个客户,张三、李四、王二狗、狗剩,他们各开设一个银行账户,账户能记录他们的名字和账户余额,账户有存钱和查询两个功能。

如果我们采用传统的方法,我们需要定义2×4共8个变量,每个人用一个变量代表名字,一个变量代表余额。我们还需要一个函数,用来实现存钱。

我们给这4个人进行开卡、存入一些钱,然后查账:

  1. name_1 = 'John San'
  2. name_2 = 'Leese'
  3. name_3 = 'Wanna Go'
  4. name_4 = 'Go Some'
  5.  
  6. balance_1 = 0
  7. balance_2 = 0
  8. balance_3 = 0
  9. balance_4 = 0
  10.  
  11. def deposit(balance, amt):
  12. # 存钱函数
  13. return balance + amt
  14.  
  15. balance_1 = deposit(balance_1, 100) # 存钱
  16. balance_2 = deposit(balance_2, 200)
  17. balance_3 = deposit(balance_3, 50)
  18. balance_4 = deposit(balance_4, 300)
  19.  
  20. print(balance_1) # 查账
  21. print(balance_2)
  22. print(balance_3)
  23. print(balance_4)

如果人数不是4个而是40个乃至400个就很容易混淆,户主名字与账户余额不是绑定的,一不小心可能搞错了户主和账户间的对应关系。此外账户余额这类信息想改就改,完全没有安全性可言。

我们来看看利用类来实现的写法:

  1. class Bank_Account:
  2. def __init__(self):
  3. self.name = None # 户主的名字
  4. self.__balance = 0 # 账户余额(
  5.  
  6. def deposit(self, amt):
  7. # 存钱
  8. self.__balance = self.__balance + amt
  9.  
  10. def check_balance(self):
  11. # 查账
  12. print(self.__balance)
  13.  
  14. john_san = Bank_Account()
  15. john_san.name = 'John San'
  16. john_san.deposit(100)
  17.  
  18. leese = Bank_Account()
  19. leese.name = 'Leese'
  20. leese.deposit(200)
  21.  
  22. wanna_go = Bank_Account()
  23. wanna_go.name = 'Wanna Go'
  24. wanna_go.deposit(50)
  25.  
  26. go_some = Bank_Account()
  27. go_some.name = 'Go Some'
  28. go_some.deposit(300)
  29.  
  30. john_san.check_balance()
  31. leese.check_balance()
  32. wanna_go.check_balance()
  33. go_some.check_balance()

无论我们开了多少个实例,每个实例的户主名字与账户余额都是绑定的,不会弄错,此外还可以通过私有化达到一定的安全性。

所以不同的情况下,大家可以根据自己的需求选择不同的实现方法。

以上就差不多是Python类的入门内容了,实际我也暂时只接触了这么多,继承之类的还暂时没有遇到,希望这篇说的还算容易理解。继续加油,野路子码农~

野路子码农系列(2)Python中的类,可能是最通俗的解说的更多相关文章

  1. 野路子码农系列(1) 创建Web API

    新工作正式开始了2天,由于客户暂时还没交接数据过来,暂时无事可做.恰逢政佬给某超市做的商品图像识别的项目客户催收了,老板要求赶紧搞个API,于是我就想我来试试吧. 说起API,我其实是一窍不通的,我对 ...

  2. 野路子码农系列(8)我终于大致搞懂了GBDT

    由于下下周要在组里介绍一个算法,最近开始提前准备,当初非常自信地写下自己最喜欢的GBDT,但随着逐步深入,发现其实自己对这个算法的细节并不是非常了解,了解的只是一些面试题的答案而已……(既然没有深入了 ...

  3. 野路子码农系列(3)plotly可视化的简单套路

    又双叒叕要跟客户汇报了,图都准备好了吗?matplotlib出图嫌丑?那用用plotly吧,让你的图看上去经费爆炸~ P1 起因 第一次接触plotly这个库是在我们做的一个列车信号数据挖掘的项目里, ...

  4. 野路子码农(5)Python中的装饰器,可能是最通俗的解说

    装饰器这个名词一听就充满了高级感,而且很多情况下确实也不常用.但装饰器有装饰器的好处,至少了解这个对装逼还是颇有益处的.网上有很多关于装饰器的解说,但通常都太过“循序渐进”,有的还会讲一些“闭包”之类 ...

  5. python基础系列教程——Python中的编码问题,中文乱码问题

    python基础系列教程——Python中的编码问题,中文乱码问题 如果不声明编码,则中文会报错,即使是注释也会报错. # -*- coding: UTF-8 -*- 或者 #coding=utf-8 ...

  6. Python基础之:Python中的类

    目录 简介 作用域和命名空间 class 类对象 类的实例 实例对象的属性 方法对象 类变量和实例变量 继承 私有变量 迭代器 生成器 简介 class是面向对象编程的一个非常重要的概念,python ...

  7. Python中的类、对象、继承

    类 Python中,类的命名使用帕斯卡命名方式,即首字母大写. Python中定义类的方式如下: class 类名([父类名[,父类名[,...]]]): pass 省略父类名表示该类直接继承自obj ...

  8. python中的类和实例

    今天花了两个多小时后搜索相关博客看了看python中有关类和实例的介绍,差不多大概明白了. python中的类和c++中的类是一样的,不同之处就是c++的类,如果含有成员变量,并且成员变量发生变化后, ...

  9. 4、Python中的类详解(0601)

    <大话数据结构>的作者程杰在博客园也有博客,网址是:http://cj723.cnblogs.com/ 面向对象编程(OOP) 1.程序 = 指令 + 数据 代码可以选择以指令为核心或以数 ...

随机推荐

  1. wav格式文件、pcm数据

    wav格式文件是常见的录音文件,是声音波形文件格式之一,wav 文件由文件头和数据体两部分组成. 文件头是我们在做录音保存到文件的时候,要存储的文件的说明信息,播放器要通过文件头的相关信息去读取数据播 ...

  2. Centos7 使用 kubeadm 安装Kubernetes 1.13.3

    目录 目录 什么是Kubeadm? 什么是容器存储接口(CSI)? 什么是CoreDNS? 1.环境准备 1.1.网络配置 1.2.更改 hostname 1.3.配置 SSH 免密码登录登录 1.4 ...

  3. nginx笔记----解决windows80端口被iis占用

    打开注册表:regedit HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\HTTP 数值数据修改成0或者其他 然后重启

  4. js坚持不懈之14:不要在文档加载之后使用 document.write()示例

    在看w3school的JavaScript教程时,关于文档输出流中有这么一句话:绝不要在文档加载之后使用 document.write().这会覆盖该文档. 不太明白什么意思,找了一个例子: < ...

  5. IOS开发证书常见问题

    1.本地Provisioning Profiles存放路径 ~/Library/MobileDevice/Provisioning Profiles 2.this action could not b ...

  6. ubuntu18.04修改网卡名称为eth0

    1.修改grub文件 vim /etc/default/grub 查找 GRUB_CMDLINE_LINUX="" 修改为 GRUB_CMDLINE_LINUX="net ...

  7. java 位运算符 以及加法 交换两个变量值

    先给出十转二的除法 2       60 30       0 15 0 7 1 3 1 1  1 0  1 60转二 111100 再介绍位运算符 a=60 b=13 A = 0011 1100 B ...

  8. 你想知道的3D Touch开发全在这里了

    前言 iPhone 6s和iPhone 6s Plus为多点触摸界面带来了强大的3D触摸新维度.这项新技术可以感知用户按下显示屏的深度,让他们比以往任何时候都更能使用你的应用程序和游戏.更多关于3D ...

  9. express+sequelize 做后台

    第一部分:安装express 第一步:执行 npm install -g express-generator note:必须安装这个,不然创建express项目的时候会提示express命令没有找到 ...

  10. Hybrid App—Hybrid App开发模式介绍和各种开发模式对比

    什么是Hybrid App 最开的App开发只有原生开发这个概念,但自从H5广泛流行后,一种效率更高的开发模式Hybrid应运而生,它就是"Hybrid模式".Hybrid APP ...