類的成員:
成員有以下:
1疏旨、字段: 可分為靜態(tài)字段 普通字段
2贝咙、方法: 可分為靜態(tài)方法 類方法 普通方法
3虑啤、特性/屬性 普通屬性
注:所有成員中鸳慈,只有普通字段的內(nèi)容保存對象中饱溢,即:根據(jù)此類創(chuàng)建了多少對象,在內(nèi)存中就有多少個普通字段走芋。而其他的成員绩郎,則都是保存在類中,即:無論對象的多少翁逞,在內(nèi)存中只創(chuàng)建一份肋杖。
一、字段
靜態(tài)字段在內(nèi)存中只保存一份挖函。
普通字段在每個對象中都要保存一份状植。
類可以直接訪問靜態(tài)字段,不能直接訪問普通字段怨喘。
二津畸、方法
方法包括:普通方法、靜態(tài)方法和類方法必怜,三種方法在內(nèi)存中都?xì)w屬于類肉拓,區(qū)別在于調(diào)用方式不同。
普通方法:由對象調(diào)用梳庆;至少一個self參數(shù)暖途;執(zhí)行普通方法時卑惜,自動將調(diào)用該方法的對象賦值給self;
類方法:由類調(diào)用丧肴; 至少一個cls參數(shù)残揉;執(zhí)行類方法時胧后,自動將調(diào)用該方法的類復(fù)制給cls芋浮;
靜態(tài)方法:由類調(diào)用;無默認(rèn)參數(shù)壳快;
三纸巷、屬性
對于屬性property的使用方法
class Foo:
def __init__(self):
pass
@property
def property_func(self):
return 'xxx'
f = Foo()
f.property_func ###屬性的調(diào)用 不需要加括號
注意:
屬性存在的意義是:訪問屬性時可以制造出和訪問字段完全相同的假象。
屬性用法一眶痰、
class A:
def func(self):
return 'xxx'
BAR = property(func)
a = A()
res = a.BAR #####自動調(diào)用property中的個的func方法瘤旨,并獲取返回值
print(res)
屬性的三種用法(新式類):
以下來Python源碼
property(fget=None, fset=None, fdel=None, doc=None) -> property attribute
fget is a function to be used for getting an attribute value, and likewise
fset is a function for setting, and fdel a function for del'ing, an
attribute. Typical use is to define a managed attribute x:
class C(object):
def getx(self): return self._x
def setx(self, value): self._x = value
def delx(self): del self._x
x = property(getx, setx, delx, "I'm the 'x' property.")
# Decorators make defining new properties or modifying existing oneseasy:
class C(object):
@property
def x(self): #####獲取屬性
"I am the 'x' property."
return self._x
@x.setter
def x(self, value): ###設(shè)置值時,一定要有兩個參數(shù)竖伯。
self._x = value
@x.deleter ##刪除屬性
def x(self):
del self._x
Django 的視圖中 request.POST 就是使用的靜態(tài)字段的方式創(chuàng)建的屬性
請看以下代碼
class WSGIRequest(http.HttpRequest):
def __init__(self, environ):
script_name = get_script_name(environ)
path_info = get_path_info(environ)
if not path_info:
# Sometimes PATH_INFO exists, but is empty (e.g. accessing
# the SCRIPT_NAME URL without a trailing slash). We really need to
# operate as if they'd requested '/'. Not amazingly nice to force
# the path like this, but should be harmless.
path_info = '/'
self.environ = environ
self.path_info = path_info
# be careful to only replace the first slash in the path because of
# http://test/something and http://test//something being different as
# stated in http://www.ietf.org/rfc/rfc2396.txt
self.path = '%s/%s' % (script_name.rstrip('/'),
path_info.replace('/', '', 1))
self.META = environ
self.META['PATH_INFO'] = path_info
self.META['SCRIPT_NAME'] = script_name
self.method = environ['REQUEST_METHOD'].upper()
self.content_type, self.content_params = cgi.parse_header(environ.get('CONTENT_TYPE', ''))
if 'charset' in self.content_params:
try:
codecs.lookup(self.content_params['charset'])
except LookupError:
pass
else:
self.encoding = self.content_params['charset']
self._post_parse_error = False
try:
content_length = int(environ.get('CONTENT_LENGTH'))
except (ValueError, TypeError):
content_length = 0
self._stream = LimitedStream(self.environ['wsgi.input'], content_length)
self._read_started = False
self.resolver_match = None
def _get_scheme(self):
return self.environ.get('wsgi.url_scheme')
@cached_property
def GET(self):
# The WSGI spec says 'QUERY_STRING' may be absent.
raw_query_string = get_bytes_from_wsgi(self.environ, 'QUERY_STRING', '')
return http.QueryDict(raw_query_string, encoding=self._encoding)
def _get_post(self): ##############################
if not hasattr(self, '_post'):
self._load_post_and_files()
return self._post
def _set_post(self, post): ##################################
self._post = post
@cached_property
def COOKIES(self):
raw_cookie = get_str_from_wsgi(self.environ, 'HTTP_COOKIE', '')
return http.parse_cookie(raw_cookie)
@property
def FILES(self):
if not hasattr(self, '_files'):
self._load_post_and_files()
return self._files
POST = property(_get_post, _set_post) ###########################
isinstance 和 issubclass
isinstance(obj,cls)檢查是否obj是否是類 cls 的對象
class Foo(object):
pass
obj = Foo()
isinstance(obj, Foo)
issubclass(sub, super)檢查sub類是否是 super 類的派生類
class Foo(object):
pass
class Bar(Foo):
pass
issubclass(Bar, Foo)
str和repr
直接上代碼
class Foo(object):
def __init__(self,name):
self.name = name
def __str__(self):
return '%s str method in class Foo'%self.name
def __repr__(self):
return '%s repr method in class Foo'%self.name
a = Foo('egon')
print('%s in Foo'%a)
print('%r in Foo'%a)
注意:
1存哲、在一個類中,如果這兩種方法都存在七婴,在執(zhí)行打印實例對象的時候?qū)?zhí)行str祟偷。
2、在語法糖的幫助下打厘,%r 將會調(diào)用repr方法修肠。
反射(自省):
class Foo:
f = '類的靜態(tài)變量'
def __init__(self,name,age):
self.name=name
self.age=age
def say_hi(self):
print('hi,%s'%self.name)
obj=Foo('egon',73)
#獲取屬性 getattr
print(getattr(obj,'asdf','doesn\'t exist')) ####若對象中的屬性不存在户盯,則打印最后后面的結(jié)果嵌施。
#檢測是否含有屬性 hasattr
print(hasattr(obj,'name')) #注意,第二個參數(shù)要是字符串莽鸭。
print(hasattr(obj,'say_hi'))
# 為對象設(shè)置屬性
setattr(obj,'sb',True)
setattr(obj,'show_name',lambda self:self.name + 'sb')
print(obj.__dict__)
print(obj.show_name(obj))
#刪除對象屬性
delattr(obj,'age')
delattr(obj,'sdf') ### 屬性不存在則報錯
反射也適用于類
class Foo(object):
staticField = "old boy"
def __init__(self):
self.name = 'wupeiqi'
def func(self):
return 'func'
@staticmethod
def bar():
return 'bar'
print(getattr(Foo, 'staticField'))
print(getattr(Foo, 'func'))
print(getattr(Foo, 'bar'))
getitem吗伤、setitem、delitem
class Bar(object):
def __getitem__(self, key):
print('__getitem__', key)
def __setitem__(self, key, value):
print('__setitem__', key, value)
def __delitem__(self, key):
print('__delitem__', key)
obj = Bar()
result = obj['k1'] # 自動觸發(fā)執(zhí)行 __getitem__
obj['k2'] = 'parker' # 自動觸發(fā)執(zhí)行 __setitem__
del obj['k1'] # 自動觸發(fā)執(zhí)行 __delitem__
綁定方法與非綁定方法
類中定義的函數(shù)分成兩大類
綁定方法(綁定給誰硫眨,誰來調(diào)用就自動將他本身當(dāng)作第一個參數(shù)傳入)
1牲芋、綁定到類的方法:用classmethod裝飾器裝飾的方法,為類量身定制類. bound_method(), 自動將類當(dāng)作第一個參數(shù)傳入捺球,(其實對象也能調(diào)用缸浦,但仍將類當(dāng)作第一個參數(shù)傳入)
2、綁定到對象的方法:沒有被任何裝飾器裝飾的方法氮兵。
為對象量身定制裂逐。對象.bound_method(),自動將對象當(dāng)作第一個參數(shù)傳入
(屬于類的函數(shù)泣栈,類可以調(diào)用卜高,但是必須按照函數(shù)的規(guī)則來弥姻,沒有自動傳值這么一說)
非綁定方法:用staticmethod裝飾器裝飾的方法
1、不與類或?qū)ο蠼壎ú籼危惡蛯ο蠖寄苷{(diào)用庭敦,但是沒有自動傳值這么一說,就是一個普通工具而已
注意:與綁定到對象的方法區(qū)分開薪缆,在類中直接定義的函數(shù)秧廉,沒有被任何裝飾器裝飾的,都是綁定到對象的方法拣帽,可不是普通函數(shù)疼电,對象調(diào)用方法會自動傳值,而staticmethod裝飾的方法减拭,不管誰來調(diào)用蔽豺,都沒有自動傳值一說
class
How to get the type of an object without using the type() function?
a = '111'
print(a.__class__)
slots
1.slots是什么:是一個類變量,變量值可以是列表,元祖,或者可迭代對象,也可以是一個字符串(意味著所有實例只有一個數(shù)據(jù)屬性)
2.引子:使用點來訪問屬性本質(zhì)就是在訪問類或者對象的dict屬性字典(類的字典是共享的,而每個實例的是獨立的)
3.為何使用slots:字典會占用大量內(nèi)存,如果你有一個屬性很少的類,但是有很多實例,為了節(jié)省內(nèi)存可以使用slots取代實例的dict
當(dāng)你定義slots后,slots就會為實例使用一種更加緊湊的內(nèi)部表示。實例通過一個很小的固定大小的數(shù)組來構(gòu)建,而不是為每個實例定義一個
字典,這跟元組或列表很類似拧粪。在slots中列出的屬性名在內(nèi)部被映射到這個數(shù)組的指定小標(biāo)上修陡。使用slots一個不好的地方就是我們不能再給實例添加新的屬性了,只能使用在slots中定義的那些屬性名。
4.注意事項:slots的很多特性都依賴于普通的基于字典的實現(xiàn)可霎。另外,定義了slots后的類不再 支持一些普通類特性了,比如多繼承魄鸦。大多數(shù)情況下,你應(yīng)該只在那些經(jīng)常被使用到 的用作數(shù)據(jù)結(jié)構(gòu)的類上定義slots比如在程序中需要創(chuàng)建某個類的幾百萬個實例對象 。
關(guān)于slots的一個常見誤區(qū)是它可以作為一個封裝工具來防止用戶給實例增加新的屬性啥纸。盡管使用slots可以達(dá)到這樣的目的,但是這個并不是它的初衷号杏。 更多的是用來作為一個內(nèi)存優(yōu)化工具。
class A:
__slots__ = ['Parker','Zhang']
def __init__(self):
pass
a = A()
a.Parker = 1
# a.zhang = 12 ##無法給實例添加新屬性
# print(a.__dict__) #報錯
print(a.__slots__) ######['Parker', 'Zhang']
總結(jié): slots的好處: 節(jié)省內(nèi)存斯棒,由slots代替dict
enter和exit (上下文管理器)
官網(wǎng)鏈接:
https://docs.python.org/2/library/stdtypes.html
Python’s with
statement supports the concept of a runtime context defined by a context manager. This is implemented using two separate methods that allow user-defined classes to define a runtime context that is entered before the statement body is executed and exited when the statement ends.
An example of a context manager that returns itself is a file object. File objects return themselves from enter() to allow open()
to be used as the context expression in a with
statement.
Exit the runtime context and return a Boolean flag indicating if any exception that occurred should be suppressed. If an exception occurred while executing the body of the with
statement, the arguments contain the exception type, value and traceback information. Otherwise, all three arguments are None
.
Returning a true value from this method will cause the with
statement to suppress the exception and continue execution with the statement immediately following the with
statement. Otherwise the exception continues propagating after this method has finished executing. Exceptions that occur during execution of this method will replace any exception that occurred in the body of the with
statement.
The exception passed in should never be reraised explicitly - instead, this method should return a false value to indicate that the method completed successfully and does not want to suppress the raised exception. This allows context management code (such as contextlib.nested
) to easily detect whether or not an __exit__()
method has actually failed.
代碼:
class Open(object):
def __init__(self,name):
self.name = name
def __enter__(self):
print('enter 方法')
def __exit__(self, exc_type, exc_val, exc_tb):
print('exit 方法') #######4來到__exit__方法
print(exc_type)
print(exc_val)
print(exc_tb)
return True ####return True 后 盾致,with后面的代碼會正常執(zhí)行,打印 'the final'
with Open('a.txt') as f: ####1.首先進(jìn)入__enter__方法
print('with 方法') #####2.來到with 方法
sss ######3.拋出異常 (要是不拋出異常荣暮,exit方法中的三個參數(shù)值為None)
print('the final')
1.使用with語句的目的就是把代碼塊放入with中執(zhí)行庭惜,with結(jié)束后,自動完成清理工作穗酥,無須手動干預(yù)
2.在需要管理一些資源比如文件护赊,網(wǎng)絡(luò)連接和鎖的編程環(huán)境中,可以在exit中定制自動釋放資源的機(jī)制砾跃,你無須再去關(guān)系這個問題骏啰,這將大有用處
實際應(yīng)用:
class Open(object):
def __init__(self,name,mode):
self.name = name
self.mode = mode
def __enter__(self):
self.opened = open(self.name,self.mode) # 在enter方法中打開文件并返回文件名稱
return self.opened # 返回值就是as后面的writer
def __exit__(self, exc_type, exc_val, exc_tb): #在 close方法中關(guān)閉文件
self.opened.close()
with Open('mytext.txt','w') as writer:
writer.write('hello world')
裝飾器實現(xiàn)上下文管理
from contextlib import contextmanager
@contextmanager
def open_file(filename,mode):
try:
f = open(filename,mode)
yield f
finally:
f.close()
with open_file('bbb.txt','w') as f:
f.write('I am so awesome')
print(f.closed)
iter方法
類實例化出的對象本身不可被循環(huán),當(dāng)在父類中加入iter方法后抽高,遍歷一個對象將會執(zhí)行此方法:
class Reverse(object):
def __init__(self,data):
self.data = data
def __iter__(self):
for i in self.data:
yield i
data = Reverse([1,2,3,4])
for datum in data:
print(datum)
class Reverse(object):
def __init__(self,data):
self.data = data
self.index = len(data)
def __iter__(self):
return self
def __next__(self):
if self.index == 0:
raise StopIteration
self.index -= 1
return self.data[self.index]
rev = Reverse([1,2,3,4])
i = iter(rev)
for char in i:
print(char)
判斷函數(shù)與方法的區(qū)別:
from types import FunctionType,MethodType
class Foo(object):
def __init__(self):
pass
def func(self):
pass
obj = Foo()
print(isinstance(obj.func,MethodType)) #True
print(isinstance(obj.func,FunctionType)) #False
print(isinstance(Foo.func,MethodType)) #False
print(isinstance(Foo.func,FunctionType)) #True
方法判耕,無需傳入self參數(shù)
函數(shù),必須手動傳self參數(shù)
元類
class MyType(type):
def __init__(self, *args,**kwargs):
print(11)
super().__init__(self)
def __call__(self, *args, **kwargs):
print(22)
obj = self.__new__(self, *args, **kwargs)
self.__init__(obj,*args)
return obj
class Foo(metaclass=MyType):
def __init__(self, name):
print(44)
self.name = name
def __new__(cls, *args, **kwargs):
print(33)
return object.__new__(cls)
# 第一階段:解釋器從上到下執(zhí)行代碼創(chuàng)建Foo類
# 第二階段:通過Foo類創(chuàng)建obj對象
obj = Foo('Parker')
print(obj)
print(obj.name)
元類總結(jié)翘骂,Python中一切皆對象壁熄,類本質(zhì)上也是對象帚豪,有type創(chuàng)建產(chǎn)生,因此上述代碼可以理解為:
1草丧、在解釋器執(zhí)行到class Foo時狸臣,就會觸發(fā)type的init方法,因而創(chuàng)建Foo這個類
2昌执、在實例化Foo時烛亦,由于Foo(),所以執(zhí)行創(chuàng)建其類(type)的call方法仙蚜,call方法將會調(diào)用init方法來來執(zhí)行call方法此洲,再由call方法來觸發(fā)new方法厂汗,創(chuàng)建類后委粉,開始執(zhí)行Foo類的init方法,初始化對象娶桦。
isinstance 和 type
判斷對象是否屬于一個類時贾节,isinstance() 不如type準(zhǔn)確
對象之間的相加
class Foo(object):
def __init__(self,age):
self.age = age
def __add__(self, other):
return Foo(obj1.age + other.age)
obj1 = Foo(11)
obj2 = Foo(12)
obj3 = obj1 + obj2
print(obj3.age)
私有屬性的方法
class A(object):
__age = 23
def __init__(self,name):
self.__name = name
a = A('Parker')
print(a._A__age) # 對象._類__字段
print(a._A__name)