>>a=[1,2,3]>>>b=[1,2,3]>>>a==bTrue#a和b是否是同一個對象>>>aisbFalse#a和b的地址其實是不一樣的>>>id(a)4498717128>>>id(b)4446861832在比較時但也有例外。Python對一些常用的值進行緩存優化" />

日韩久久久精品,亚洲精品久久久久久久久久久,亚洲欧美一区二区三区国产精品 ,一区二区福利

Python中面向對象你應該知道的一下知識

系統 1836 0

0x00 is與==

==運算符是比較兩個對象的內容是否相等,默認情況是調用對象的__eq__方法進行比較;而is是比較兩個對象是否一樣,它比較的兩個對象的id,即它們的內存地址是否相同。

            
>>> a = [1,2,3]
>>> b = [1,2,3]
>>> a == b
True
# a和b是否是同一個對象
>>> a is b
False
# a和b的地址其實是不一樣的
>>> id(a)
4498717128
>>> id(b)
4446861832
          

在比較時但也有例外。Python對一些常用的值進行緩存優化,例如在區間[-5,256]的整數,它們在創建時,無論創建多少個對象,它們的id是一樣的,即它們在底層中只保存一份內存。

            
>>> a = -5
>>> b = -5
>>> a == b
True
>>> a is b
True
>>> a = -6
>>> b = -6
>>> a == b
True
>>> a is b
False
          

對一些短的字符串也是如此,因此并不是所有字符串都會創建新的實例

            
>>> a='123'
>>> b='123'
>>> a==b
True
>>> a is b
True
>>> id(a)
4446903800
>>> id(b)
4446903800
>>> x = 'long char'
>>> y = 'long char'
>>> x == y
True
>>> x is y
False
          

0x01 __repr__與__str__

每個類都應該提供一個__repr__方法。__repr__方法和__str__方法有什么不一樣呢?
簡單的說,__repr__可以反映一個對象的類型以及包含的內容,而__str__主要是用于打印一個對象的內容。例如看一下Python中的日期類datetime

            
import datetime
>>> today = datetime.date.today()
>>> today
datetime.date(2019, 7, 7)
>>> print(today)
2019-07-07
>>> str(today)
'2019-07-07'
>>> repr(today)
'datetime.date(2019, 7, 7)'
          

__str__在字符串連接,打印等操作會用到,而__repr__主要是面向開發者,它能反饋的信息比較多,例如在交互環境下輸入today這個變量會打印出datetime.date(2019, 7, 7),不僅可以看出today代表的是今天的日期信息,還可以看出它的類型信息。更重要的是你可以直接復制這段打印出來的信息,直接構造一個“相同”的對象出來。

例如

            
>>> now = datetime.date(2019, 7, 7)
>>> now
datetime.date(2019, 7, 7)
          

0x02 對象復制

對象的復制或說對象拷貝可以分為淺拷貝和深拷貝。

淺拷貝與深拷貝

我們通過代碼來說明,就很好理解

如果要拷貝的對象是基本數據類型,那么深拷貝和淺拷貝的區別不是很大。

            
>>> a = [1,2,3]
>>> b = list(a)
>>> a[1]=200
>>> a
[1, 200, 3]
>>> b
[1, 2, 3]
          

修改a中的元素并不會影響到b

但如果要拷貝的對象包含了另一個對象,那么就要考慮深拷貝和淺拷貝的問題了。

            
>>> a = [[1,2,3],[4,5,6],[7,8,9]]
>>> b = list(a)
>>> a == b
True
>>> a is b
False
          

這里有一個列表a,里面有三個子列表,即列表里包含的是對象。

我們使用list工廠方法創建了一個a的拷貝b,這個b就是a的淺拷貝,為什么呢?

            
>>> a[1][2]='x'
>>> a
[[1, 2, 3], [4, 5, 'x'], [7, 8, 9]]
>>> b
[[1, 2, 3], [4, 5, 'x'], [7, 8, 9]]
          

把a[1][2]的元素修改成了x,這時候b列表中也響應了相同的修改。所以這是淺拷貝,因為沒有把子對象進行拷貝,只是拷貝了指向子對象的引用。

知道淺拷貝,那么深拷貝就很好理解了。執行拷貝之后,拷貝對象和原對象是完全獨立的,修改任何一個對象都不會影響到另一個對象

如何深拷貝一個對象

這時候就需要copy模塊了,該模塊有兩個重要的方法deepcopy和copy。不錯,就是分別代表深拷貝和淺拷貝。

            
>>> import copy
>>> a = [[1,2,3],[4,5,6],[7,8,9]]
>>> b = copy.deepcopy(a)
>>> a
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> b
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> a[1][2]='change'
>>> a
[[1, 2, 3], [4, 5, 'change'], [7, 8, 9]]
>>> b
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
          

執行深拷貝之后,對a的修改并不會影響到b。

0x03 Abstract Base Classes(ABC)

抽象基類的使用

為了說明為什么要使用ABC,我們先看下不使用ABC的情況

            
# 定義了基類Base
>>> class Base:
	def foo(self):
		raise NotImplemented
	def bar(self):
		raise NotImplemented

# 定義實現類
>>> class Concrete(Base):
	def foo(self):
		print('called foo')
  
  # 實現類并沒有實現bar方法
	
>>> c = Concrete()
>>> c.foo()
called foo
>>> c.bar()
Traceback (most recent call last):
 File "
            
              ", line 1, in 
              
                
  c.bar()
 File "
                
                  ", line 5, in bar
  raise NotImplemented
TypeError: exceptions must derive from BaseException
                
              
            
          

可以看到沒有實現基類bar方法的Concrete類,依然可以使用,并沒有在第一時間拋出錯誤。

要解決這個問題,就要用到我們abc模塊了。

            
from abc import ABC
# 定義基類,繼承于ABC
>>> class Base(ABC):
	@abstractmethod
	def foo(self):
		pass
	@abstractmethod
	def bar(self):
		pass

	
>>> class Concrete(Base):
	def foo(self):
		print("called foo")
	# 實現類并沒有實現bar方法

>>> c = Concrete()
Traceback (most recent call last):
 File "
            
              ", line 1, in 
              
                
  c = Concrete()
TypeError: Can't instantiate abstract class Concrete with abstract methods bar
              
            
          

可以看到,在使用Concrete構造方法的時候,就立即拋出TypeError了。

0x04 使用namedtuple的好處

關于namedtuple的用法在前面的文章《如何在Python中表示一個對象》 也有提到。

簡單的說namedtuple是一個可以命名的tuple,他是對tuple的擴展,它有跟tuple一樣不可變的屬性。

對于一些數據類的定義,namedtuple使用起來非常方便

            
>>> from collections import namedtuple
>>> Point = namedtuple('Point','x y z')
>>> p = Point(1,3,5)
>>> p
Point(x=1, y=3, z=5)
>>> p.x
1
>>> p.y = 3.5
AttributeError: can't set attribute
# 可以看出通過namedtuple定義對象,就是一個class類型的
>>> type(p)

            
          

還可以使用它內部的一些工具方法,在實際的編碼當中也是非常實用的。

            
# 轉化為dict
>>> p._asdict()
OrderedDict([('x', 1), ('y', 3), ('z', 5)])
# 更新或替換某個屬性值
>>> p._replace(x=111)
Point(x=111, y=3, z=5)
# 使用_make創建新對象
>>> Point._make([333,666,999])
Point(x=333, y=666, z=999)
          

0x05 類變量和實例變量

Python中對象的屬性類型有實例變量和類變量。

類變量是屬于類的,它存儲在“類的內存空間”里,并能夠被它的各個實例對象共享。而實例變量是屬于某個特定實例的,它不在“類的內存空間”中,它是獨立于各個實例存在的。

            
>>> class Cat:
	num_legs = 4
	
>>> class Cat:
	num_legs = 4
	def __init__(self,name):
		self.name = name

>>> tom = Cat('tom')
>>> jack = Cat('jack')
>>> tom.name,jack.name
('tom', 'jack')
>>> tom.num_legs,jack.num_legs
(4, 4)
>>> Cat.num_legs
4
          

這里定義了一個貓類,它有一個實例變量name,還有一個類變量num_legs,這個是各個實例共享的。

如果對類變量進行,那么其它實例也會同步修改。而對某個實例對修改,并不會影響都類變量。

            
>>> Cat.num_legs = 6
>>> tom.num_legs,jack.num_legs
(6, 6)
>>> tom.num_legs = 2
>>> tom.num_legs,jack.num_legs
(2, 6)
>>> Cat.num_legs
6
          

tom.num_legs = 2這個語句都作用其實對tom這個實例增加了一個屬性,只不過這個屬性名稱跟類屬性的名稱是一致的。

0x06 實例方法、類方法和靜態方法

為了更好區分,我們還是來看代碼

            
>>> class MyClass:
	def method(self):
		print(f"instance method at {self}" )
	@classmethod
	def classmethod(cls):
		print(f'classmethod at {cls}')
	@staticmethod
	def staticmethod():
		print('staticmethod')

		
>>> mc = MyClass()
>>> mc.method

            
              <__main__.MyClass object at 0x10c280b00>>
>>> mc.classmethod

              
                
                  >
>>> mc.staticmethod

                  
                
              
            
          

可以看到在MyClass中分別定義實例方法(method)、類方法(classmethod)和靜態方法(staticmethod)

在Python中一切都是對象,所以我打印來一下各個方法的__repr__輸出。

對于實例方法method是綁定在MyClass的具體實現對象中的,而類方法classmethod是綁定在MyClass中的,而靜態方法staticmethod既不綁定在實例里,也不綁定在類中,它就是一個function對象實例方法的調用,需要傳遞一個實例對象到實例方法中,以下兩種方法的調用是等價的。

            
>>> mc.method()
instance method at <__main__.MyClass object at 0x10910ada0>
>>> MyClass.method(mc)
instance method at <__main__.MyClass object at 0x10910ada0>
          

類方法的調用和靜態方法的調用都是使用ClassName.methodName()的方式。

            
>>> MyClass.classmethod()
classmethod at 
            
              
>>> MyClass.staticmethod()
staticmethod
            
          

類方法和靜態方法有什么區別呢?

  • 首先在前面可以看到類方法和靜態方法的對象是不一樣的,一個是bound method,一個是function。
  • 其次類方法可以訪問到類對象MyClass,而靜態方法不能。
  • 最后靜態方法其實跟一個普通的function對象一樣,只不過它是屬于類命名空間的。

0x07 總結一下

本文主要對Python中一些常見的面向對象的相關的一些特性進行了說明。包括對象的比較、輸出、拷貝等操作,以及推薦使用namedtuple定義數據類。最后對類變量和實例變量以及類方法、實例方法和靜態方法的不同作了分析。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 泸水县| 西乡县| 黑山县| 南投市| 汶川县| 岑巩县| 福鼎市| 洛隆县| 邵武市| 贡嘎县| 林甸县| 涞水县| 资阳市| 杭州市| 广元市| 河西区| 滨州市| 长顺县| 石嘴山市| 岳西县| 咸阳市| 新宁县| 堆龙德庆县| 横峰县| 铜鼓县| 阳山县| 隆昌县| 贡觉县| 驻马店市| 临桂县| 晋江市| 南城县| 景宁| 通渭县| 望城县| 罗山县| 金湖县| 宜州市| 友谊县| 兴安县| 凉山|