1. 程式人生 > >關於python下劃線命名的事兒以及magic變數相關

關於python下劃線命名的事兒以及magic變數相關

前言

先扯一點背景知識

在這份編碼規範中的“命名規範-命名風格”這一節的最後,提到了對幾種使用前置和後置下劃線的,對變數的比較特殊的命名方式:

  • 單下劃線開頭:弱內部使用標識,無法被from M import *所引用
  • 單下劃線結尾:避免和python關鍵字衝突,可以加個後置下劃線
  • 雙下劃線開頭:類成員變數中的私有變數,
  • 雙下劃線開頭,雙下劃線結尾:這是magic物件或屬性的名字,永遠不要將這樣的命名方式應用於自己的變數和函式

本文主要關注正是以上第四種--python自動在使用者名稱空間建立的magic變數

1、__name__變數

__name__屬性是直接內建在.py檔案中的。

  • 如果直接執行.py檔案,__name__將被設定為__main__。
  • 如果.py檔案是被import,__name__將被設定為.py檔案的名字

這個屬性經常用來當做一個使用模式的標識:

#a.py
print 'a function'
if __name__=='__main__':
    print 'a test'
------------------------------
#b.py
import a

如果執行python a.py將打印出兩行內容,執行python b.py只會打印出'a function'。一般可以把只針對a.py的測試程式碼寫在if __name__=='__main__',因為如果a.py被其他的指令碼import之後,這部分程式碼將不會被執行。可以很安全的對a.py進行單獨的測試。

2、__file__變數

__file__可以用來獲取python指令碼的“路徑+指令碼名稱”,這可能是一個相對路徑也可能是一個絕對路徑,取決按照什麼路徑來執行的指令碼,一般來說__file__變數和os.path配合,可以用來獲取python指令碼的絕對路徑:

#a.py
import os
print os.path.realpath(__file__)
out>>E:\Eclipse_workspace\python_learn\a.py

3、__import__函式

python匯入模組時,一般使用import,而import語句其實也是呼叫builtin函式:__import__()實現的匯入,直接使用__import__比較少見,除非匯入的模組是不確定的,需要在執行時才能確定匯入哪些模組,可以使用__import__,預設接收需要匯入的模組名的字串:

#a.py
def f1():
    print 'f1'
def f2():
    print 'f2'
#b.py
model=__import__('a')
model.f1()
model.f2()

在memfs的測試中,我的每一個測試case就是一個獨立的.py檔案,在確定需要測試哪些case後,在執行時才‘動態的’去import相應的case,就是通過__import__來實現的。

4、__str__函式

__str__是一個比較常用的內建函式,在定義類的時候經常使用,__str__函式返回一個字串,這個字串就是此物件被print時顯示的內容,(如果不定義這個函式,將會顯示預設的格式:<__main__.A object at 0x0000000001FB7C50>):

#a.py
import datetime
import os
class A(object):
    def __str__(self):
        #返回當前的日期
        return str(datetime.datetime.now())
a=A()
print a
time.sleep(1)
#每次列印A()的物件,都返回當前的時間
print a
out>>2015-06-25 15:01:01.573000
out>>2015-06-25 15:01:02.573000

這個函式在django的model類中如果定義的話,print一條資料庫中的資料,可以指定顯示任何的值:

class Question(models.Model):
#定義一個數據庫表,其中包含question_id和question_text
#....
def __str__(self):
    #只想顯示question_text
    return self.question_text

注:在python3.x中str被廢棄,使用unicode

5、__init__物件函式

__init__比較常見,是物件的初始化函式,例子如下:

#a.py
class A(object):
    pass
class B(A):
    #B類繼承自A,如果要重寫__init__,需要先呼叫父類的__init__
    def __init__(self,*args):
        super(B,self).__init__(*args)

6、__new__物件函式

__new__()函式是類建立物件時呼叫的內建函式,必須返回一個生成的物件,__new__()函式在__init__()函式之前執行。一般來說沒有比較過載這個函式,除非需要更改new物件的流程,有一種場景“單例模式”要求只能存在一個class A的物件,如果重複建立,那麼返回的已經建立過的物件的引用。可以這樣使用__new__函式:

a.py
class A(object):
    def __new__(cls):
        if not "_instance" in vars(cls):
            cls._instance=super(A,cls).__new__(cls)
        return cls._instance
a=A()
b=A()
print id(a)==id(b)
out>>True

可以看出,a和b其實引用了同一個物件

7、__class__物件變數

instance.__class__表示這個物件的類物件,我們知道在python中,類也是一個物件(好理解麼),例:

#a.py
class A(object):
    pass
a=A()
B=a.__class__
b=B()
print type(b)
out>><class '__main__.A'>

可以看出,a是A類的一個物件,a.__class__就是A類,將這個類賦值給B,使用B()又可以創建出一個物件b,這個物件b也是A類的物件,(暈了麼?),這個__class__有什麼卵用呢?下面的例子就可以用到

8、__add__物件函式

這其實是一類函式,包括__sub__,__mul__,__mod__,__pow__,__xor__,這些函式都是對加、減、乘、除、乘方、異或、等運算的過載,是我們自定義的物件可以具備運算功能:

#a.py
class A(object):
    def __init__(self,v):
        self.v=v
    def __add__(self,other):
        #建立建立一個新的物件
        x=self.__class__(self.v+2*other.v)
        return x
a=A(1)
b=A(2)
c=a+b
print c.v
ouot>>5

這樣我們就定義了一個加法操作1+2=1+2*2=5

9、__doc__文件字串

python建議在定義一個類、模組、函式的時候定義一段說明文字,例子如下:

#c.py
"""
script c's doc
"""
class A(object):
    """
    class A's doc
    """
    pass
def B():
    """
    function B's doc
    """
    pass
print __doc__
print A.__doc__
print B.__doc__
out>>script c's doc
out>>class A's doc
out>>function B's doc

呼叫別的模組、函式的時候如果不清楚使用方法,也可以直接檢視doc文件字串

10、__iter__和next函式

凡是可以被for....in的迴圈呼叫的物件,我們稱之為可以被迭代的物件,list,str,tuple都可以被迭代,它們都實現了內部的迭代器函式,比如說list,tuple,字串這些資料結構的迭代器如下:

a=[1,2,3,4]
b=('i',1,[1,2,3])
print a.__iter__()
print b.__iter__()
out>><listiterator object at 0x0000000001CC7C50>
out>><tupleiterator object at 0x0000000001CC7B00>

如果我們要實現一個我們自己的迭代器物件,那麼我們必須實現兩個預設的方法:__iter__和next。

__iter__()函式將會返回一個迭代器物件,next()函式每次被呼叫都返回一個值,如果迭代完畢,則raise一個StopIteration的錯誤,用來終止迭代。下面的例子將實現一個可以迭代的物件,輸出a~z的26個字母,該物件接收一個int引數用來表示輸出字母的數量,如果該引數超過字母表的長度,則迴圈從‘a-z’再次進行迴圈輸出:

import random
class A(object):
    def __init__(self,n):
        self.stop=n
        self.value=0
        #字母列表
        self.alph=[chr(i) for i in range(97,123)]
    def __iter__(self):
        return self

    def next(self):
        #如果超過長度超過26則重置
        if self.value==len(self.alph):
            self.value=0
            self.stop=self.stop-len(self.alph)
        #最終,已完成n個字元的輸出,則結束迭代
        if self.value>self.stop:
            raise StopIteration    
        x=self.alph[self.value]
        self.value+=1
        return x

for i in A(1000):
    print i,
out>>a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k .....

11、__dict__、__slot__和__all__

這三個變數有一些關係,__dict__在類和物件中都存在,它是一個包含變數名和變數的字典,見以下的例子:

#a.py
class A(object):
    c=3
    d=4
    def __init__(self):
        self.a=1
        self.b=2
    def func(self):
        pass
print A().__dict__
print A.__dict__
out>>{'a': 1, 'b': 2}
out>>{'__module__': '__main__', 'd': 4, 'c': 3, 'func': <function func at 0x00000000021F2BA8>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None, '__init__': <function __init__ at 0x00000000021F2AC8>}

一個物件的__dict__只包含self定義的變數,而一個類的__dict__包含了類裡面的函式(func函式)、類變數,以及很多隱性的變數,包括__dict__變數本身也是隱性的。

__slot__變數的用法理解起來比較要難一點,正常的情況下,我們例項化一個物件,可以給這個物件增加任意的成員變數,即使不在類裡面定義的變數都可以,如下:

#a.py
class A(object):

    def __init__(self):
        self.a=1
        self.b=2

a=A()
#給a增加一個x變數
a.x=1
#也可以給a增加一個匿名函式
a.y=lambda x,y:x*y
print a.x
print a.y(3,5)
out>>1
out>>15

如果我們想限制一下物件繫結的變數,我們可以在類定義的時候增加一個slots變數,這個變數是一個字串的元組,例子如下:

class A(object):
    __slots__=('a','b','x')
    def __init__(self):
        self.a=1
        self.b=2

        pass
    #__slots__=('a','b',)
    def func(self):
        pass
a=A()
a.x=1
#執行到a.y時會報錯:AttributeError: 'A' object has no attribute 'y'
a.y=lambda x,y:x*y
print a.y(3,5)

__all__變數是一個字串列表,它定義了每一個模組會被from module_name import *這樣的語句可以被import的內容(變數,類,函式)

#a.py 不定義__all__
class A(object):
    def __init__(self):
        self.a=1
        self.b=2

    def func(self):
        pass
def B():
    pass

c=10

#b.py
from a import *
print A
print B
print c
out>><class 'learn_draft.A'>
out>><function B at 0x00000000021D1438>
out>>10

如果在a.py中定義__all__=['A','c'],則B函式對於b.py來說是不可見的。

12、__hash__

雜湊函式,在python中的物件有一個hashable(可雜湊)的概念,對於數字、字串、元組來說,是不可變的,也就是可雜湊的,因此這些物件也可以作為字典的key值。另外,列表、字典等,是可變物件,因此也就是不可雜湊的,也就不能作為字典的key值。是否可雜湊,可以呼叫內建函式hash()進行計算,hash()函式返回計算的到的hash值。

  • 完全相同的變數,呼叫雜湊演算法的到的hash值一定是相同的

當然一般來說,我們不會去重新定義一個物件的__hash__函式,除非我們想實現一個自定義的需求,在stackoverflow有人提出這樣一個需求,需要判斷有相同詞頻的字串是相等的,也就是說“abb”和“bab”這樣的字串是相等的,這個時候我們可以繼承字串類,然後重寫雜湊函式,如下:

import collections

class FrequencyString(str):
    @property
    def normalized(self):
        try:
            return self._normalized
        except AttributeError:
            self._normalized = normalized = ''.join(sorted(collections.Counter(self).elements()))
            return normalized

    def __eq__(self, other):
        return self.normalized == other.normalized

    def __hash__(self):
        return hash(self.normalized)

13、__getattr__和__setattr__,__delattr__物件函式

先介紹兩個內建函式,getattr()和setattr(),使用這兩個函式可以獲取物件的屬性,或者給物件的屬性賦值:

#a.py
class A(object):
    def __init__(self):
        self.a=1
        self.b=2
a=A()
setattr(a,'a',3)
print a.a
print getattr(a,'b')
out>>3
out>>2

其實使用這兩個函式和直接訪問a.a,a.b沒有任何區別,但好處是setattr和getattr接受兩個字串去確定訪問物件a的哪一個屬性,和__import__一樣,可以在執行時在決定去訪問物件變數的名字,在實際工作中經常會使用這兩個函式。

__getattr__()這個函式是在訪問物件不存在的成員變數是才會訪問的,見下面的例子:

class A(object):
    def __init__(self):
        self.a=1
        self.b=2

    def func(self):
        pass
    def __getattr__(self,name):
        print 'getattr'
        return self.a

a=A()
print a.d
out>>getattr
out>>1

在呼叫a.d時,d不是a的成員變數,則python會去查詢物件是否存在__getattr__()函式,如果存在,則返回__getattr__()函式的返回值,我們這裡返回的是self.a的值1。

由於__getattr__()的特性,我們可以將__getattr__()設計成一個公共的介面函式,在autotest的proxy.py中就看到了這樣的用法:

class ServiceProxy(object):

def __init__(self, serviceURL, serviceName=None, headers=None):
    self.__serviceURL = serviceURL
    self.__serviceName = serviceName
    self.__headers = headers or {}

def __getattr__(self, name):
    if self.__serviceName is not None:
        name = "%s.%s" % (self.__serviceName, name)
    return ServiceProxy(self.__serviceURL, name, self.__headers)

#呼叫的時候,op是執行的特定操作的字串,op傳入__getattr__將會把ServiceProxy物件重新的內部變數重新賦值,然後返回一個更新之後的物件
function = getattr(self.proxy, op)

__setattr__和__getattr__不一樣,物件的所有屬性賦值,都會經過__setattr__()函式,看下面的例子:

class A(object):
    def __init__(self):
        self.a=1
        self.b=2

    def func(self):
        pass
    def __getattr__(self,name):
        print 'getattr'
        return self.a
    def __setattr__(self, name, value):
        print 'setattr %s' % name
        if name == 'f':
            return object.__setattr__(self,name,value+1000)
        else:
            return object.__setattr__(self,  name, value)

a=A()
a.f=1000
print a.f
out>>setattr a
out>>setattr b
out>>setattr f
out>>2000

從輸出可以看到init函式的self.a和self.b的賦值也經過了__setattr__,而且在賦值的時候我們自定義了一個if邏輯,如果name是‘f’,那麼value會增加1000,最終的a.f是2000

__delattr__不舉例了,刪除一個物件屬性用的。

14、__call__物件函式

如果一個物件實現了__call__()函式,那麼這個物件可以認為是一個函式物件,可以使用加括號的方法來呼叫,見下面例子:

class A(object):
    def __init__(self):
        self.li=['a','b','c','d']
    def func(self):
        pass
    def __call__(self,n):
        #返回li列表的第n個元素
        return self.li[n]

a=A()
#a可以當做函式一樣呼叫
print a(0),a(1),a(2)
out>>a b c

在實際工作中__call__函式非常有用,可以把一個物件變成callable的物件

http://www.cnblogs.com/zhuangxiu/p/4797245.html

相關推薦

關於python劃線命名事兒以及magic變數相關

前言 先扯一點背景知識 在這份編碼規範中的“命名規範-命名風格”這一節的最後,提到了對幾種使用前置和後置下劃線的,對變數的比較特殊的命名方式: 單下劃線開頭:弱內部使用標識,無法被from M import *所引用單下劃線結尾:避免和python關鍵字衝突,可以加個後置下劃線雙下劃線開頭:類成員變數中的

python:類5——Python 的類的劃線命名有什麽不同?

以及 mod 導入 類成員 部分 span 子類 內部函數 默認 首先是單下劃線開頭,這個被常用於模塊中,在一個模塊中以單下劃線開頭的變量和函數被默認當作內部函數,如果使用 from a_module import * 導入時,這部分變量和函數不會被導入。不過值得註意的

理解Python的雙劃線命名(轉)

函數 python的函數 del 標準 開頭 變量名 cnblogs 通過 全局 add by zhj:今天在學習SimpleHTTPServer的源代碼時,看到了Python標準庫SocketServer模塊中有個BaseServer類,該類的__init__方法定義如下

理解Python的雙劃線命名

public bject () 有變 weakref _weak 令行 方法 全部 引子 我熱情地邀請大家猜測下面這段程序的輸出: class A(object): def __init__(self): self.__priva

python劃線,私有變數

  轉自:http://blog.sina.com.cn/s/blog_58649eb30100g4zo.html Python用下劃線作為變數字首和字尾指定特殊變數。 " 單下劃線" 開始的成員變數叫做保護變數,意思是隻有類物件和子類物件

python中那些雙劃線開頭得函式和變數

Python 用下劃線作為變數字首和字尾指定特殊變數 _xxx 不能用’from module import *’匯入 __xxx__ 系統定義名字 __xxx 類中的私有變數名 核心風格:避免用下劃線作為變數名的開始。 因為下劃線對直譯器有特殊的意義,而且是內建識別符號所使用的符號,我們建議程式設計師避

駝峰命名劃線命名互轉php實現

res rst 命名 turn case rds rto step class 駝峰命名和下劃線命名經常需要互轉,下面提供兩種php的實現方式.第一種方法效率相對差一些,實現方式如下: //駝峰命名轉下劃線命名 function toUnderScore($str

js物件屬性駝峰式命名(帶數字)轉劃線命名

將類似於 info 格式轉化為 info2  var info = { id: 1, id1: 2, userName1: '劉玄德', userName2: '劉玄德', userName3: '大哥',

劃線命名轉換為駝峰命名(根據介面名查詢介面檔名)

見到一段程式碼實現的功能是:通過介面名驗證介面檔案是否存在。 舉個例子:介面名為:aa_bb_cc 介面檔名為 AbcAaBbCc.php 取這段程式碼稍作改造: <?php class FindFilename { /** $prefix 檔案字首 $Interfa

修改TabLayout劃線寬度,以及在Api28遇到的問題—— tabLayout.getDeclaredField 空指標以及水波紋背景問題

在API28之前,我們修改TabLayout下劃線寬度,程式碼如下: /** * 設定tabLayout下劃線的寬 */ public static void setIndicator(TabLayout tabs, int leftDip, int rightD

python 劃線

以單下劃線開頭,表示這是一個保護成員,只有類物件和子類物件自己能訪問到這些變數。以單下劃線開頭的變數和函式被預設當作是內部函式,使用from module improt *時不會被獲取,但是使用import module可以獲取 以單下劃線結尾僅僅是為了區別該名稱與關鍵詞

Java駝峰命名劃線命名轉換

import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 駝峰命名轉換 * * @author 47475 * */ public class Tool { private static Patte

window系統Node.js安裝以及環境變數配置

一、Node.js安裝 1.首先在Node官網上下載對應的安裝包,我這裡下載的是64位window系統的安裝檔案node-v10.15.0-x64.msi 2. 點選安裝檔案,開始node.js安裝 3. 點選下一步 4. 勾選同意選項,並點選下一步

駝峰,連線符,劃線命名等互相轉換 CaseFormat.LOWER_CAME等

1 jar: guava-r05.jar 2 String orderColumn = "orderColumn"; //輸入是LOWER_CAMEL,輸出是LOWER_UNDERSCORE orderColumn = CaseForm

使用Java反射機制將Bean物件轉換成Map(駝峰命名方式 — 劃線命名方式)

1、駝峰與下劃線方式名稱互相轉換工具類 public class CamelUnderlineUtil { private static final char UNDERLINE ='_'; public static String camelToUnderline(

VS Code英漢詞典外掛v0.0.4-駝峰劃線命名 2018-11-09

首先, 在兩天時間內安裝數破百, 多謝支援. VS Code外掛市場地址: 英漢詞典 - Visual Studio Marketplace 開源庫地址同前文: Visual Studio Code外掛-英漢詞典初版釋出 查詢單詞功能基本不變, 在詳細資訊的開頭添加了原詞:

textview、edittext劃線邊框,以及圓弧邊框的新增

學習應該是一件被嚴謹對待的事情,近期正在做一個專案,如今算是告一段落,打算慢慢地將專案中用到的瑣碎的東西貼出來留作記憶以及與大家分享,好了不多廢話,上效果以及程式碼: 效果如圖所示: 所需要的是各種形狀的邊框以及下劃線,程式碼如下: 在工程中新建drawable資料夾,

字串的劃線命名和駝峰命名轉換

/** * 將駝峰式命名的字串轉換為下劃線大寫方式。如果轉換前的駝峰式命名的字串為空,則返回空字串。</br> * 例如:HelloWorld->HELLO_WORLD * @param name 轉換前的駝峰式命名的字串 * @retu

Java-駝峰命名劃線命名互轉

/*** * 下劃線命名轉為駝峰命名 * * @param para * 下劃線命名的字串 */ public static String UnderlineToHu

關於centos7java的安裝以及環境變數的配置

現在我們常見的一些關於Linux的系統很多,但是使用的更多的一般都是CentOS和Ubuntu,今天我就來記錄一下關於centos下java的安裝和環境變數的配置。首先使用xshell連線到centos7,我使用的是centos7 *64位,所以一切的配置安裝都按照的是cen